Spring使用 基本的JavaBean 来完成以前只可能由EJB完成的事情,是个分层架构。Spring创建bean都需要通过 读取 、 解析 、 校验配置文件, 然后注册创建成Bean。 Spring是一个Bean容器 , 主要作用是替我们管理bean对象 (简单的Java类对象的生命周期)。不管框架如何强大,还是需要我们程序员来告诉其一些必要信息的(比如要 管理的bean对象的类相关信息、是否开启组件扫描 等),这些我们称之为对 Spring框架的配置 ,目前主流的配置方式是 通过使用配置文件或注解。
Spring中最核心的两个类: DefaultListableBeanFactory、XmlBeanDifinitionReader。DefaultListableBeanFactory 是整个bean加载的核心部分,是Spring注册及加载bean的默认实现 。XmlBeanDefinitionReader 主要使用reader属性对资源文件进行读取和注册。
XML配置文件读取是Spring中重要的功能,大部分Spring大部分功能都是 以配置作为切入点 。 XmlBeanFactory 继承自 DefaultListableBeanFactory ,而对于 DefaultListableBeanFactory 不同的地方其实是在 XmlBeanFactory 中使用了自定义的XML读取器 XmlBeanDefinitionReader ,主要用于从XML文档中读取 BeanDefinition, 实现了个性化的 BeanDefinitionReader 读取, DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry 接口。
Spring的配置文件读取是通过ClasaPathResource进行封装的 ,如:new ClassPathResource("beanxml")。在java中, 将不同来源的资源的读取逻辑抽象成URL ,通过注册不同的 handler来处理。 一般handler的类型使用不同的前缀,URL没有默认定义相对的path路径,也 没有提供相关方法对资源进行检查 ,顾Spring对其内部需要使用到的资源做了属于自己的抽象结构, 用Resource接口来封装底层资源。
Resource 接口继承 InputStreamSource(封装了任何能返回InputStream的类)。
Resource接口抽象了所有Spring内部使用到的底层资源 ,首先它定义了3个能判断当前资源状态的方法: 存在性(exists)、可读性(isReadable)、是否处于打开状态(isOpen) 。有了Resource接口便可以对所有资源进行统一处理。 ClassPathResource 中的实现是通过class或 classLoader 提供的底层方法进行调用。以此完成对配置文件资源的封装。
当通过Resource相关类完成了对配置文件进行封装,接下来由 XmlBeanDefinitionReader 完成对配置文件的读取工作。
XML文件的验证模式有两种:DTD、XSD(XML Schema)
DTD即文档类型定义, 是一种XML约束模式语言,是XML文件的验证机制 。是一种保证XML文档格式正确的有效方法, 可以通过比较XML文档和DTD文件来查看文档是否符合规范,元素和标签的使用是否正确 。一个DTD文档包含:元素的定义规则、元素间关系的定义规则、元素可使用的属性、可使用的实体或符号规则。 要使用DTD验证模式需要在XML文件的头部声明。
XML Schema语言就是XSD。 XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。也可以 通过XML Schema指定一个XML文档所允许的结构和内容 。XML Schema本身也是一个XML文档,符合XML语法结构,可以用通用的XML解析器解析它。
使用XML Schema文档对XML实例进行校验,要声明名称空间和指定该名称空间所对应的XML Schema文档存储位置 。通过schemaLocation属性来指定名称空间所对应的XML Schema文档的存储地址(1、名称空间URL;2、该名称空间所标识的XML Schema文件地址或URL地址)。
另外验证模式通过 XmlBeanDefinitionReader 中的setValidationMode方法进行设定。而 Spring 用来检测验证模式的方法实际上就是判断是否包含 DOCTYPE ,如果包含就是 DTD ,否则就是 XSD 。
XML文件经过验证模式,交由DocumentLoader进行解析成对应的 Document。 而解析的过程中存在这么一环节:(EntityResolver) 根据声明去寻找对应的DTD定义,以便对文档进行验证认证 。也可以通过setEntityResolver设置DTD定义。EntityResolver它用来接收两个参数publicId和systemId,xsd格式文件通常publicId为null。而对于不同的验证模式采用不同的解析器进行解析,并把文件转换成Document文件,用于提取及注册bean。
Document 文件通过 BeanDefinitionDocumentReader 进行内部逻辑处理,并提取root用于作为参数继续完成BeanDefinition的注册。
通过AbstractApplicationContext#refresh()提供了在加载完配置文件后的启动过程。以SpringBoot的Web环境来看启动过程(文末有spring容器的启动过程及区别)
1prepareRefresh():由于在此时还没有加载servlet容器,所以servletContext=null,servletConfig=null,还没有进行初始化propertySource,添加了几个ApplicationListeners。
2obtainFreshBeanFactory():创建BeanFactory,即DefaultListableBeanFactory
3prepareBeanFactory():对DefaultListableBeanFactory进行属性填充。如:添加类加载器,添加BeanPostProcessor等等。
4postProcessBeanFactory():在上面prepareBeanFactory()公共的之后,提供子类的特殊处理,注册特殊的后处理器,此处为ServletWebServerApplicationContext的具体实现。
5invokeBeanFactoryPostProcessors():主要是扫描包,注册成BeanDefinition,对类配置的信息解析成BeanDefinition的属性。然后执行实现了BeanFactoryPostProcessor接口的bean的postProcessBeanFactory()方法。
对于BeanDefinitionRegistryPostProcessor类型的BeanFactoryPostProcessor,执行postProcessBeanDefinitionRegistry
ConfigurationWarningsApplicationContextInitializer类型的processor,检查包名是否异常
获取类型为BeanDefinitionRegistryPostProcessor并且是PriorityOrdered优先排序类型的beanDefinition
上面获取postProcessorNames的具体实现如下
调用刚获取到的currentRegistryProcessor列表中的bean的postProcessBeanDefinitionRegistry()方法
获取到启动类
对启动类开始进行解析,包装成SourceClass,开始解析
获取到需要扫描的包信息,开始进行解析
解析是否有includeFilters,excludeFilters,lazyInit等属性,解析basePackage,basePackageClasses配置,如果没有,则根据启动类的包名获取,最终获得需要扫描的包。
解析包下所有beanDefinition,遍历并逐个解析。 如果实现了AnnotatedBeanDefinition接口,解析是否有lazy,Primary,DependsOn,Role,Description等注解并对beanDefinition进行相应属性设置。 检查是否在beanDefinitionMap中,如果不存在则将beanDefinition注册到beanDefinitionMap中。
对获取到的包下的beanDefinition进行遍历处理。处理是否import,ImportResource,Bean注解,处理实现的接口。
处理完BeanDefinitionRegistryPostProcessor类型的bean之后,处理之前注册的处理器,前两个处理器没做任何处理,第三个处理器ConfigurationClassPostProcessor,对目前加载的所有的beanDefinition进行处理。如果是启动类,生成代理类并替换原来的beanClass
获取实现了BeanFactoryPostProcessor接口的类,去除了上面已经解析过的internalConfigurationAnnotationProcessor,根据是否排序进行分类,然后按照PriorityOrdered,Ordered,nonOrdered以此执行。
6registerBeanPostProcessors():获取实现了BeanPostProcessor的bean,并根据是否实现order接口进行排序,并按顺序注册到beanPostProcessors中。
7initMessageSource():
8initApplicationEventMulticaster():注册事件广播器
9onRefresh():获取ServletWebServerFactory(tomcatServletWebServerFactory),创建TomcatWebServer,然后initServletPropertySources
创建TomcatWebServer之后获取需要初始化的servlet,filter
将servlet添加到servletContext中
将filter添加到servletContext中
10registerListeners():在事件广播器中注册监听器,注册监听器name,如果earlyApplicationEvents存在,则广播事件。
11finishBeanFactoryInitialization():实例化bean
先进行实例化之前的 *** 作,再实例化bean。
如果有实现InstantiationAwareBeanPostProcessor, 则执行postProcessBeforeInstantiation
根据实例化策略进行实例化(这里是cglib)包装成BeanWrapper
实例化之后执行后处理
执行postProcessProperties
属性设置,在AutowiredAnnotationBeanPostProcessor中进行依赖注入
是否实现BeanNameAware,BeanClassLoaderAware,BeanFactoryAware等接口
init之前
调用自定义的init方法,在InitDestroyAnnotationBeanPostProcessor中处理
如果实现了InitializingBean,执行afterPropertiesSet()方法
然后执行init之后的方法
12finishRefresh():主要做的事情是启动tomcat,并发布事件
逐个listener去匹配事件
以下为spring环境启动与web环境启动的相同和不同:
1prepareRefresh():区别在于initPropertySources()的实现不同,spring上下文为AnnotationConfigApplicationContext,web上下文AnnotationConfigServletWebServerApplicationContext
2obtainFreshBeanFactory():相同
3prepareBeanFactory():相同
4postProcessBeanFactory():spring使用的AbstractApplicationContext的默认处理,没有提供实现,web提供了实现注册了特殊的处理器
5invokeBeanFactoryPostProcessors():相同
6registerBeanPostProcessors():相同
7initMessageSource():相同
8initApplicationEventMulticaster():相同
9onRefresh():spring什么都没做,使用的AbstractApplicationContext的默认实现,web提供了创建web容器的过程
10registerListeners():相同
11finishBeanFactoryInitialization():相同
12finishRefresh():spring主要做的事情是发布事件,web容器多了一步启动web容器
综上:web环境和spring环境启动容器流程基本相同,spring默认使用AbstractApplicationContext的实现,web多了properties解析,注册特殊处理器,创建webServer和启动webServer的过程。
当在properties文件中写入 一些值时,我们可以通过在类的属性上使用@Value注解,取到这个值。
如在配置文件中写入
则在类中
这样可以获取到值。但是根据网上的例子,我一直获取不到,首先根据spring的例子,
想要使用@Value,必须把当前类加入spring的容器管理, 如果使用注解,就是在类上加入
@Controller @Service @Component 等容器注解,可是我加入了@Component注解,依然不能获取到值,但是如果是在@Controller 下,则可以。翻阅多篇博客论坛后,终于找到原因。
原来,使用@Value的类,在spring中,不能直接通过new *** 作符来使用,而是应该通过spring的注解 @Autowired 来使用,
如
这样才能成功通过@Value注解取到配置文件中的值。
1、什么是Spring
Spring就是一个java写的框架,使用java语言开发的, 轻量级的, 开源的框架。 可以在j2se、j2ee项目中都可以使用。
Spring核心技术: ioc, aop
Spring又叫做:容器, spring作为容器, 装的是java对象。 可以让spring创建java对象, 给属性赋值。
Spring作用: 实现解耦合, 解决java对象之间的耦合, 解决模块之间的耦合。
总结: Spring最大的特点就是帮你创建对象,不需要你在代码中手动的去new,而是只要你告诉Spring的容器,你要的对象的名字,Spring容易就会自动给你了。就好比如你想吃一个东西,你不需要自己手动的去做,你可以去餐厅里面,告诉服务员你想吃的菜的名字,那么服务员就可以给你上菜了。
Spring也像一艘航空母舰,虽然它本身没有什么作战能力。但是它的甲板上可以容纳很多舰载机,具体作战任务可以让这些舰载机去执行。舰载机就像是其它框架的核心对象,比如nybatis的SqlSession对象。Spring可以管理这些核心的对象。
2、Spring的有点
Spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器管理对象,容器是装东西的,Spring 容器不装文本,数字。装的是对象。Spring 是存储对象的容器。
(1) 轻量 Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring核心功能的所需的 jar 总共在 3M 左右。 Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar
(2) 针对接口编程,解耦合 Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
(4) 方便集成各种优秀框架 Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。
3、IoC 控制反转
31IoC分为 控制和反转
反转:把开发人员管理对象的权限转移给了代码之外的容器实现。 由容器完成对象的管理。
正转:开发人员在代码中, 使用 new 构造方法创建对象。 开发人员掌握了对象的创建,属性赋值,对象从开始到销毁的全部过程。 开发人员有对 对象 全部控制。
通过容器,可以使用容器中的对象(容器已经创建了对象, 对象属性赋值了, 对象也组装好了)。
Spring就是一个容器,可以管理对象,创建对象,给属性赋值。
31IoC的技术实现
DI ( 依赖注入) :Dependency Injection, 缩写是DI 是IoC的一种技术实现。 程序只需要提供要使用的对象的名称就可以了, 对象如何创建, 如何从容器中查找,获取都由容器内部自己实现。
Spring框架使用的DI实现IoC通过spring框架, 只需要提供要使用的对象名词就可以了。 从容器中获取名称对应的对象。spring底层使用的 反射机制, 通过反射创建对象,给属性。
以上就是关于Spring源码解析(一)- 容器的基本实现全部的内容,包括:Spring源码解析(一)- 容器的基本实现、Spring容器启动流程、解决Spring 使用 @Value 取不到值等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)