Spring Bean 中的生命周期和获取方式详解
作者:程序猿进阶
一、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
<bean id="myBean" class="com.taobao.pojo"></bean>
【2】获取配置文件:这里使用最原始的方式获取。
Resource resource = new ClassPathResource("spring-core.xml")
【3】 利用 XmlBeanFactory 解析并注册 bean 定义:已经完成将配置文件包装成了 Spring 定义的资源,并触发解析和注册。XmlBeanFactory 实际上是对 DefaultListableBeanFactory(非常核心的类,它包含了基本 IOC 容器所具有的重要功能,是一个 IOC 容器的基本实现。然后是调用了this.reader.loadBeanDefinitions(resource)
,从这里开始加载配置文件) 和 XmlBeanDefinitionReader 组合使用方式的封装,所以这里我们仍然将继续分析基于 XmlBeanFactory 加载 bean 的过程。
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
Spring 使用了专门的资源加载器对资源进行加载,这里的 reader 就是 XmlBeanDefinitionReader
对象,专门用来加载基于 XML 文件配置的 bean。这里的加载过程为:
①、利用 EncodedResource 二次包装资源文件;
②、获取资源输入流,并构造 InputSource 对象:
// 获取资源的输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); // 构造InputSource对象 InputSource inputSource = new InputSource(inputStream); // 真正开始从 XML文件中加载 Bean定义 return this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
这里的 this.doLoadBeanDefinitions(inputSource, encodedResource.getResource())
就是真正开始加载 XMl 的入口,该方法源码如下:第一步获取 org.w3c.dom.Document 对象,第二步由该对象解析得到 BeanDefinition 对象,并注册到 IOC 容器中。
protected intdoLoadBeanDefinitions(InputSource inputSource, Resource resource){ try { // 1. 加载xml文件,获取到对应的Document(包含获取xml文件的实体解析器和验证模式) Document doc = this.doLoadDocument(inputSource, resource); // 2. 解析Document对象,并注册bean return this.registerBeanDefinitions(doc, resource); } }
③、获取 XML 文件的实体解析器和验证模式:this.doLoadDocument(inputSource, resource)
包含了获取实体解析器、验证模式,以及 Document 对象的逻辑,XML 是半结构化数据,XML 的验证模式用于保证结构的正确性,常见的验证模式有 DTD 和 XSD 两种。
④、加载 XML 文件,获取对应的 Document 对象和验证模式与解析器,解析器就可以加载 Document 对象了,这里本质上调用的是 DefaultDocumentLoader
的 loadDocument() 方法,源码如下:整个过程类似于我们平常解析 XML 文件的流程。
public DocumentloadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { 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)
中,源码如下:
public intregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{ // 使用DefaultBeanDefinitionDocumentReader构造 BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); // 记录之前已经注册的BeanDefinition个数 int countBefore = this.getRegistry().getBeanDefinitionCount(); // 加载并注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回本次加载的bean的数量 return getRegistry().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); // 解析并注册BeanDefinition this.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 instanceof Element) { 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 Bean 的生命周期和获取方式详解的文章就介绍到这了,更多相关Spring Bean 生命周期内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!