0


Spring Bean 的生命周期和获取方式

优质博文:IT-BLOG-CN

一、Spring Bean 的生命周期,如何被管理的

对于普通的 Java对象,当 new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而由 Spring IoC容器托管的对象,它们的生命周期完全由容器控制。Spring 中每个 Bean的生命周期如下:

主要对几个重要的步骤进行说明:
【1】实例化 Bean: 对于 BeanFactory容器,当客户向容器请求一个尚未初始化的 bean时,或初始化 bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean进行实例化。对于 ApplicationContext容器,当容器启动结束后,便实例化所有的单实例 bean。容器通过获取 BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。实例化对象被包装在 BeanWrapper 对象中,BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制设置属性。通过工厂方法或者执行构造器解析执行即可:创建的对象是个空对象。
【2】设置对象属性(依赖注入): 实例化后的对象被封装在 BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。紧接着获取所有的属性信息通过 populateBean(beanName,mbd,bw,pvs),Spring 根据 BeanDefinition 中的信息进行依赖注入。并且通过 BeanWrapper提供的设置属性的接口完成依赖注入。赋值之前获取所有的 InstantiationAwareBeanPostProcessor 后置处理器的 postProcessAfterInstantiation() 第二次获取InstantiationAwareBeanPostProcessor 后置处理器;执行 postProcessPropertyValues()最后为应用 Bean属性赋值:为属性利用 setter 方法进行赋值 applyPropertyValues(beanName,mbd,bw,pvs)。
【3】bean 初始化: initializeBean(beanName,bean,mbd)。
 1)执行xxxAware 接口的方法,调用实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口的方法。
 2)执行后置处理器之前的方法:applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)所有后置处理器的 BeanPostProcessor.postProcessBeforeInitialization()。
 3)执行初始化方法: InitializingBean 与 init-methodinvoke 当 BeanPostProcessor的前置处理完成后就会进入本阶段。先判断是否实现了 InitializingBean接口的实现;执行接口规定的初始化。其次自定义初始化方法。

InitializingBean 接口只有一个函数:afterPropertiesSet()这一阶段也可以在 bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前 bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。若要使用它,我们需要让 bean实现该接口,并把要增加的逻辑写在该函数中。然后 Spring会在前置处理完成后检测当前 bean是否实现了该接口,并执行 afterPropertiesSet函数。当然,Spring 为了降低对客户代码的侵入性,给 bean的配置提供了 init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring 便会在初始化阶段执行我们设置的函数。init-method 本质上仍然使用了InitializingBean接口。
 4)applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);执行初始化之后的后置处理器的方法。BeanPostProcessor.postProcessAfterInitialization(result, beanName);
【4】Bean的销毁: DisposableBean 和 destroy-method:和 init-method 一样,通过给 destroy-method 指定函数,就可以在bean 销毁前执行指定的逻辑。

Bean 的管理就是通过 IOC容器中的 BeanDefinition信息进行管理的。

二、Spring Bean 的加载和获取过程

Bean 的加载过程,主要是对配置文件的解析,并注册 bean 的过程 。

【1】根据注解或者 XML中 定义 Bean 的基本信息。例如:spring-core.xml

<beanid="myBean"class="com.taobao.pojo"></bean>

【2】获取配置文件:这里使用最原始的方式获取。

Resource resource =newClassPathResource("spring-core.xml")

【3】 利用 XmlBeanFactory 解析并注册 bean 定义:已经完成将配置文件包装成了 Spring 定义的资源,并触发解析和注册。XmlBeanFactory 实际上是对 DefaultListableBeanFactory(非常核心的类,它包含了基本 IOC 容器所具有的重要功能,是一个 IOC 容器的基本实现。然后是调用了

this.reader.loadBeanDefinitions(resource)

,从这里开始加载配置文件) 和 XmlBeanDefinitionReader 组合使用方式的封装,所以这里我们仍然将继续分析基于 XmlBeanFactory 加载 bean 的过程。

XmlBeanFactory beanFactory =newXmlBeanFactory(resource);

Spring 使用了专门的资源加载器对资源进行加载,这里的 reader 就是

XmlBeanDefinitionReader 

对象,专门用来加载基于 XML 文件配置的 bean。这里的加载过程为:

①、利用 EncodedResource 二次包装资源文件;
②、获取资源输入流,并构造 InputSource 对象:

// 获取资源的输入流InputStream inputStream = encodedResource.getResource().getInputStream();// 构造InputSource对象InputSource inputSource =newInputSource(inputStream);// 真正开始从 XML文件中加载 Bean定义returnthis.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

这里的

this.doLoadBeanDefinitions(inputSource, encodedResource.getResource())

就是真正开始加载 XMl 的入口,该方法源码如下:第一步获取 org.w3c.dom.Document 对象,第二步由该对象解析得到 BeanDefinition 对象,并注册到 IOC 容器中。

protectedintdoLoadBeanDefinitions(InputSource inputSource,Resource resource){try{// 1. 加载xml文件,获取到对应的Document(包含获取xml文件的实体解析器和验证模式)Document doc =this.doLoadDocument(inputSource, resource);// 2. 解析Document对象,并注册beanreturnthis.registerBeanDefinitions(doc, resource);}}

③、获取 XML 文件的实体解析器和验证模式:

this.doLoadDocument(inputSource, resource) 

包含了获取实体解析器、验证模式,以及 Document 对象的逻辑,XML 是半结构化数据,XML 的验证模式用于保证结构的正确性,常见的验证模式有 DTD 和 XSD 两种。
④、加载 XML 文件,获取对应的 Document 对象和验证模式与解析器,解析器就可以加载 Document 对象了,这里本质上调用的是

DefaultDocumentLoader

的 loadDocument() 方法,源码如下:整个过程类似于我们平常解析 XML 文件的流程。

publicDocumentloadDocument(InputSource inputSource,EntityResolver entityResolver,ErrorHandler errorHandler,int validationMode,boolean namespaceAware)throwsException{DocumentBuilderFactory factory =this.createDocumentBuilderFactory(validationMode, namespaceAware);DocumentBuilder builder =this.createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);}

⑤、由 Document 对象解析并注册 bean:完成了对 XML 文件的到 Document 对象的解析,我们终于可以解析 Document 对象,并注册 bean 了,这一过程发生在

this.registerBeanDefinitions(doc, resource) 

中,源码如下:

publicintregisterBeanDefinitions(Document doc,Resource resource)throwsBeanDefinitionStoreException{// 使用DefaultBeanDefinitionDocumentReader构造BeanDefinitionDocumentReader documentReader =this.createBeanDefinitionDocumentReader();// 记录之前已经注册的BeanDefinition个数int countBefore =this.getRegistry().getBeanDefinitionCount();// 加载并注册bean
    documentReader.registerBeanDefinitions(doc,createReaderContext(resource));// 返回本次加载的bean的数量returngetRegistry().getBeanDefinitionCount()- countBefore;}

这里方法的作用是创建对应的 BeanDefinitionDocumentReader,并计算返回了过程中新注册的 bean 的数量,而具体的注册过程,则是由 BeanDefinitionDocumentReader 来完成的,具体的实现位于子类 DefaultBeanDefinitionDocumentReader 中:

publicvoidregisterBeanDefinitions(Document doc,XmlReaderContext readerContext){this.readerContext = readerContext;// 获取文档的root结点Element root = doc.getDocumentElement();this.doRegisterBeanDefinitions(root);}

还是按照 Spring 命名习惯,doRegisterBeanDefinitions 才是真正干活的地方,这也是真正开始解析配置的核心所在:

protectedvoiddoRegisterBeanDefinitions(Element root){BeanDefinitionParserDelegate parent =this.delegate;this.delegate =this.createDelegate(getReaderContext(), root, parent);// 处理profile标签(其作用类比pom.xml中的profile)String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);// 解析预处理,留给子类实现this.preProcessXml(root);// 解析并注册BeanDefinitionthis.parseBeanDefinitions(root,this.delegate);// 解析后处理,留给子类实现this.postProcessXml(root);}

方法在解析并注册 BeanDefinition 前后各设置一个模板方法,留给子类扩展实现,而在

this.parseBeanDefinitions(root, this.delegate)

中执行解析和注册逻辑:方法中判断当前标签是默认标签还是自定义标签,并按照不同的策略去解析。

protectedvoidparseBeanDefinitions(Element root,BeanDefinitionParserDelegate delegate){if(delegate.isDefaultNamespace(root)){// 解析默认标签NodeList nl = root.getChildNodes();for(int i =0; i < nl.getLength(); i++){Node node = nl.item(i);if(node instanceofElement){Element ele =(Element) node;if(delegate.isDefaultNamespace(ele)){// 解析默认标签this.parseDefaultElement(ele, delegate);}else{// 解析自定义标签
                  delegate.parseCustomElement(ele);}}}}else{// 解析自定义标签
        delegate.parseCustomElement(root);}}

到这里我们已经完成了静态配置到动态 BeanDefinition 的解析,这个时候 bean 的定义已经处于内存中。

【4】 从 IOC容器加载获取 bean:我们可以调用

beanFactory.getBean("myBean") 

方法来获取目标对象。

MyBean myBean =(MyBean) beanFactory.getBean("myBean");
标签: spring java rpc

本文转载自: https://blog.csdn.net/zhengzhaoyang122/article/details/144170102
版权归原作者 程序猿进阶 所有, 如有侵权,请联系我们删除。

“Spring Bean 的生命周期和获取方式”的评论:

还没有评论