优质博文: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
<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对象,并注册beanreturn 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();// 加载并注册beandocumentReader.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);// 解析并注册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 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");