bean的生命周期
Spring容器技术内幕
如果Spring容器是一辆汽车,那么BeanFactory是汽车的发动机,而ApplicationContext是一辆完整的汽车。
Spring以容器管理所有的bean对象,容器的实体是一个BeanFactory对象。但我们常用的容器是另一个ApplicationContext,它在内部持有了BeanFactory,所有和BeanFactory相关的操作都会委托给内部的BeanFactory来完成。
ApplicationContext是一个接口,默认的实现类是AnnotationConfigApplicationContext,基于注解实现。另一种是ClassPathXmlApplicationContext,基于xml使用。
Bean生命周期控制
Bean生命周期由多个特定的生命周期阶段组成,每个生命阶段都开放了接口,允许外界对Bean施加控制。
Spring对对象的可扩展性主要就是依靠InstantiationAwareBeanPostProcessor和BeanPostProcessor来实现的。
- InstantiationAwareBeanPostProcessor。主要是作用于实例化阶段。
- BeanPostProcessor。主要作用于初始化阶段。
在Spring中可以从两个层面定义Spring的生命周期:
- Bean的作用范围。
- 实例化Bean时所经历的一系列阶段。
启动流程
- Application refresh:
- BeanFactory初始化。
- 生命周期控制。
概念
构造函数 –> 依赖注入 –> init-method
实例化,使用构造器:
- IOC功能需要是利用反射原理,反射获取类的无参构造方法创建对象,如果一个类没有无参的构造方法spring是不会创建对象的。
初始化 init-method。
- 只有一个类完整的实例被创建出来后,才能走初始化方法。
BeanFactory
是什么
BeanFactory可以理解为是个HashMap,key为BeanName,Value就是Bean的实例,并通常只提供注册(put)、获取(get)功能。
协作
如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需 要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
我们先来看一下BeanFactory类的关系图(如下所示):
BeanDefinition
实现Bean的定义(即对象的定义),且完成了对依赖的定义。BeanDefinition是配置文件<bean>元素标签在容器中的内部表示。
最重要的部分就是BeanDefinition,它完成了Bean的生成过程。一般情况下我们都是通过配置文件(xml,properties)的方式对bean进行配置,每种文件都需要实现BeanDefinitionReader,因此是reader本身现了配置文字 到bean对象的转换过程。当然我们自己也可以实现任意格式的配置文件,只需要自己来实现reader即可。
BeanDefinitionRegistry
将定义好的bean,注册到容器中(此时会生成一个注册码)。
BeanFactory
是一个bean工厂类,从中可以取到任意定义过的bean。
原理
启动容器:
了解BeanFactory当中的Bean生命周期:
- 当调用者通过
getBean(beanName)
向容器请求某一个Bean时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
接口,则在实例化Bean前,调用postProcessBeforeInstantiation()
。 - 根据配置情况调用Bean构造函数或工厂方法实例化Bean。
- 如果容器注册了
InstantiationAwareBeanPostProcessor
接口,那么在实例化Bean之后,调用该接口的postProcessAfterInstantiation()
方法,可在这里对已经实例化的对象进行装饰。 - 如果Bean配置了属性信息,那么容器将在这一步将配置值设置到Bean对应的属性中,不过在设置每个属性前将先调用
InstntiationAwareBeanPostProcessor
接口的postProcessPropertyValues()
。 - 调用Bean的属性设置方法设置属性值。
- 如果Bean实现了
org.springframework.beans.factory.BeanNameAware
接口,则将调用setBeanName()
接口方法,将配置文件中该Bean对应的名称设置到Bean中。 - 如果Bean实现了
org.springframework.beans.factory.BeanFactoryAware
接口,则将调用setBeanFactory()
,将BeanFactory容器实例设置到Bean中。 - 如果BeanFactory装配了
org.springframework.beans.factory.config.BeanPostProcessor
后处理器,则将调用BeanPostProcess的Object postProcessBeforeInitialization(Object bean,string beanName)
对Bean进行加工操作。- 入参bean是当前正在处理的Bean,beanName是当前Bean的配置名。返回加工处理后的Bean。
- 该操作可以对某些Bean进行特殊的处理甚至改变Bean的行为,其为容器提供对Bean进行后续加工处理的切入点。AOP、动态代理都通过BeanPostProcessor实现。
- 如果Bean实现了InitializingBean接口,则将调用接口的afterPropertiesSet()方法。
- 如果在<bean>中通过init-method属性定义了初始化方法,则将执行该方法。
- BeanPostProcessor后处理器的
Object postProcessAfterInitialization(Object bean,String beanName)
,这个方法此时调用容器再次获得对Bean进行加工处理的机会。 - 如果在<bean>中指定Bean的作用范围为
prototype
,则将Bean返回给调用者,调用者负责Bean后续生命的管理,Spring不再管理这个Bean的生命周期。如果作用范围为singleton
则将Bean放入Spring IOC的缓冲池中并将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命管理。 - 对于
singleton
的Bean,当容器关闭时将触发Spring对Bean后续生命周期的管理工作。如果Bean实现了DisposableBean接口,则将调用接口的destory()
,可以在此编写释放资源、记录日志等操作。 - 对于
singleton
的Bean,如果通过<bean>的destroy-method属性指定了Bean的销毁方法,则Spring会执行Bean的这个方法,完成资源的释放等操作。
源码
BeanFactory.getBean()
注册Bean。
- Spring对bean进行实例化;
- Spring将值和bean的引用注入到bean对应的属性中;
- 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
- 如果bean实现了BeanFactoryAware接口,Spring将调setBeanFactory()方法,将BeanFactory容器实例传入;
- 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
- 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
- 如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用;
- 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
- 此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
- 如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
Bean生成
Bean的生成大致可以分为两个阶段,容器启动阶段和bean实例化阶段:
容器启动阶段
只完成bean的定义:
- 加载配置文件(通常是xml文件)。
- 通过reader生成beandefinition。
- beanDefinition注册到beanDefinitionRegistry。
bean实例化阶段
完成bean的初始化:
- 当某个bean被getBean()调用时。
- bean需要完成初时化,以及其依赖对象的初始化。
- 如果bean本身有回调,还需要调用其相应的回调函数。
Spring Ioc在初始化完成之后,给了我们提供一些方法,让我们来改变一些bean的定义org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:使我们可能通过配置文件的形式,配置一些参数:
- PropertyOverrideConfigurer:则可以覆盖原本的bean参数。
- CustomEditorConfigurer:则提供类型转换支持(配置文件都是string,它需要知道转换成何种类型)。
Bean的初始化过程
如果你认为实例化的对象就是通过我们定义的类new 出来的,那就大错特错了,其实这里用到了AOP机制,生成了其代理对象(通过反射机制生成接口对象,或者是通过CGLIB生成子对象)。
- bean的具体装载过程是由beanWrapper实现的,它继承了PropertyAccessor (可以对属性进行访问)、PropertyEditorRegistry 和TypeConverter接口 (实现类型转换,就上前面说的)。
- 完成设置对象属性之后,则会检查是否实现了Aware类型的接口,如果实现了,则主动加载。
- BeanPostprocessor 可以帮助完成在初始化bean之前或之后 帮我们完成一些必要工作,比如我们在连接数据库之前将密码存放在一个加密文件,当我们连接数据库之前,需要将密码进行加载解密。只要实现 相应的接口即可:
1 | public interface BeanPostProcessor { |
- 在完成postProcessor之后,则会看对象是否定义了InitializingBean 接口,如果是,则会调用其afterProper- tiesSet()方法进一步调整对象实例的状态 ,这种方式并不常见。spring还提供了另外一种指定初始化的方式,即在bean定义中指定init-method 。
- 当这一切完成之后,还可以指定对象销毁 的一些回调,比如数据库的连接池的配置,则销毁前需要关闭连接等。相应的可以实现DisposableBean 接口或指定destroy-method。
InstantiationStrategy
InstantiationStrategy负责根据BeanDefinition对象创建一个Bean实例。
BeanWrapper
BeanWrapper相对于一个代理器,Spring委托BeanWrapper完成Bean属性的填充工作,在Bean实例被创建出来后,容器主控程序将Bean实例通过BeanWrapper保证起来。
Spring对Bean的影响
在Spring的完整生命周期当中,经过许多关键点,每个关键点涉及特定的方法调用,可大概划分为:
- Bean自身的方法。
- Bean自身的构造函数实例化、Setter设置Bean的属性值,init-method与destroy-method。
- Bean级生命周期接口方法。由Bean类直接实现:
- BeanNameAware。BeanFactory。InitializingBean。DisposableBean。等。
- 容器级生命周期接口方法。
- InstantiationAwareBeanPostProcessor与BeanPostProcessor接口,它们的实现类一般称为后处理器。
- 后处理器接口一般不由Bean自身实现,它们独立于Bean,实现类以容器附加装置的形式注册到Spring容器,通过接口反射为Spring容器扫描识别,在创建任何Bean时后处理器都会发生作用,即是全局范围影响。
- 也可以限定其对某一部分的Bean处理。
- 工厂后处理器接口方法。
- AspectJWeavingEnabler、CustomAutowrieConfigurer、ConfigurationClassPostProcessor等方法。
- 也是容器级,在应用上下文装载配置文件后立即调用。
在整个生命周期当中,Spring对Bean产生的影响主要是在两个级别上:即容器处理与类本身处理。
- 容器级别。
- InstantiationAwareBeanPostProcessor。
- BeanPostProcessor。
- 可以像插件一样注册到Spring容器,为容器提供额外的功能。
- 类本身级别。
- Constructor。
- init-method。
- 通过bean的init-method指定的初始化方法。需要在bean的配置文件中配置。
- setter。
- destroy-method。
- 类实现接口级别。需要Bean自身去实现这些接口。
- BeanNameAware。将配置文件中该Bean对应的名称设置到Bean中。
- BeanFactory。将BeanFactory容器实例设置到Bean中。
- InitializingBean。
- DisposableBean。在容器销毁后,对Bean进行后续处理操作。
- 其实不会使用这个东西。
1 | public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter{ |
Bean生命周期接口
使用Bean生命周期接口对Bean进行额外控制,虽然让Bean具有了更细致的生命周期阶段,但也使得Bean和Spring框架紧密地绑定在一起。
因此如果用户希望将业务完全POJO化,则可以只实现自己的业务接口,不需要与某个特定框架的接口关联。
可以通过<bean>的init-method和destroy-method属性配置方式为Bean指定初始化和销毁的方法。该方法与通过实现InitializingBean和DisposableBean接口达到的效果是完全相同的。
前者的实现方式可以使得Bean不需要与特定的Spring框架接口绑定,使得框架解耦。
后置处理器InitDestroyAnnotationBeanPostProcessor负责对标注了@PostConstruct、@PreDestroy的Bean进行处理。
ApplicationContext
高级容器,在BeanFactory的基础上提供更多高级功能。其与BeanFactory的区别在于:
- bean的生成方式;
- 扩展了BeanFactory的功能,提供了更多企业级功能的支持。
- ApplicationContext采用的非懒加载方式。它会在启动阶段完成所有的初始化,并不会等到getBean()才执行
- 所以,相对于BeanFactory来 说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中, ApplicationContext类型的容器是比较合适的选择。
了解ApplicationContext当中的Bean生命周期:
其与BeanFactory类似,不同点有:
- 如果Bean实现了org.springframework.context.ApplicationContext.Aware接口,则会增加应该调用该接口方法
setApplicationContext()
的步骤。 - ApplicationContexthi利用Java反射自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文。而BeanFactory需要手动调用注册。
refresh()
- registerBeanPostProcessors(beanFactory)。注册BeanPostProcessor。
- finishBeanFactoryInitialization(beanFactory)。注册余下的Singletions Bean。
ApplicationContext初始化在refresh()
方法中,并且ApplicationContext建立起来以后,其实我们是可以通过调用refresh()
这个方法重建的,这样会将原来的ApplicationContext销毁,然后再重新执行一次初始化操作。
AbstractApplicationContext是ApplicationContext的抽象实现类,该抽象类的refresh()
定义了Spring容器在加载配置文件后的各项处理过程,这些处理过程清晰地刻画了Spring容器启动时所执行的各项操作。
1 | //初始化BeanFactory |
- 初始化BeanFactory。根据配置文件实例化BeanFactory,在
obtainFreshBeanFactory()
方法中首先调用refreshBeanFactory()
方法刷新BeanFactory,然后调用getBeanFactory()
获取BeanFactory,在该步骤中Spring将配置文件的信息装入容器的Bean定义注册表(BeanDefinitionRegistry)中,但此时Bean还未初始化。 - 调用工厂后处理器。根据反射机制从BeanDefinitionRegistry中找出所有实现了BeanFactoryPostProcessor接口的Bean,调用其
postProcessBeanFactory()
方法。 - 注册Bean后处理器。根据反射机制从BeanDefinitionRegistry中找出所有实现了BeanPostProcessor接口的Bean,并将它们注册到容器Bean后处理器的注册表中。
- 初始化消息源。初始化容器的国际化消息资源。
- 初始化应用上下文事件广播器。
源码
1 |
|
prepareRefresh()
刷新前的准备工作。
1 | protected void prepareRefresh() { |
obtainFreshBeanFactory()
在ApplicationContext当中持有了一个BeanFactory,这步就是获取ApplicationContext中的 BeanFactory。在AnnotationConfigApplicationContext中其已经创建好了,直接返回即可。
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
prepareBeanFactory()
准备Bean容器,BeanFactory获取之后并不能马上使用,还要在BeanFactory中做一些准备工作,包括类加载器、表达式解析器的设置,几个特殊的BeanPostProcessor的添加等。
1 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
调用BeanFactory后置处理器
调用BeanFactoryPostProcessor的postProcessBeanFactory(beanFactory)方法,它允许在beanFactory准备完成之后对beanFactory进行一些修改,比如在bean初始化之前对beanFactory中的 beanDefinition进行修改。
注册各类Bean后置处理器
也是一个名字就体现功能的方法,把各种BeanPostProcessor注册到BeanFactory中(需要注意的是这里的注册会直接调用getBean()创建对象),BeanPostProcessor允许在bean初始化前后插手对bean的初始化过程。
初始化事件分派器
Event会有单独的篇幅详解,这里就不展开了。
初始化所有非懒加载singleton beans
这是容器刷新中最重要的方法。Spring 需要在这个阶段完成所有的 singleton beans 的实例化。这一步骤非常重要而且过程非常长,下一篇中我们来专门分析这个方法。
finishRefresh()
1 | protected void finishRefresh() { |
注册BeanPostProcessor
1 | //注册BeanPostProcessor |
通过beanFactory.getBeanNamesForType()
来获取所有BeanPostProcessor。
BeanPostProcessor按优先级分为PriorityOrdered,Ordered和其他的,对他们分别进行操作。
- 先beanFactory.getBean进性实例化,
- 再使用
sortPostProcessors()
进行排序, - 最后
registerBeanPostProcessors()
进行注册。
注册/getBean
- 先getSingleton()从缓存中获取Bean,如果没有则创建。
- 创建过程先检查有无循环依赖,有则抛出异常。
- 实例化bean前先实例化所依赖的对象。
1 | protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, |
createBean()
resolveBeforeInstantiation()
在doCreateBean()
之前调用,使用InstantiationAwareBeanPostProcessor,在Bean被创建之前处理。
1 |
|
resolveBeforeInstantiation
1 | protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { |
doCreateBean() 创建bean
1 | protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) |
createBeanInstance创建实例
populateBean设置Bean属性
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { |
initializeBean初始化方法
1 | protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { |
BeanPostProcessor
是什么
BeanPostProcessor:有两个方法postProcessBeforeInitialization()
方法和postProcessAfterInitialization()
。
概述
这个接口的作用在于对于新构造的实例可以做一些自定义的修改。比如如何构造、属性值的修改、构造器的选择等等。
只要我们实现了这个接口,便可以对构造的bean进行自定义的修改。接口定义:
1 | public interface BeanPostProcessor { |
- postProcessBeforeInitialization和postProcessAfterInitialization方法被调用的时候。这个时候bean已经被实例化,并且所有该注入的属性都已经被注入,是一个完整的bean。
- 这2个方法的返回值可以是原先生成的实例bean,或者使用wrapper包装这个实例。
应用
适用性
- 在bean初始化的时候,也就是Spring激活bean的init-method方法的时候调用。
- 在之前会调用BeanPostProcessor的postProcessBeforeInitialization方法
- 在之后会调用postProcessAfterInitialization方法。
应用场景
- AOP。
- 日常可以拓展该接口对bean初始化进行定制化处理。
InstantiationAwareBeanPostProcessor
是什么
InstantiationAwareBeanPostProcessor代表了Spring的另外一段生命周期:实例化。先区别一下Spring Bean的实例化和初始化两个阶段的主要作用:
- 实例化:实例化的过程是一个创建Bean的过程,即调用Bean的构造函数,单例的Bean放入单例池中。
- 初始化:初始化的过程是一个赋值的过程,即调用Bean的setter,设置Bean的属性。
InstantiationAwareBeanPostProcessor接口继承BeanPostProcessor接口,它内部提供了3个方法,再加上BeanPostProcessor接口内部的2个方法,所以实现这个接口需要实现5个方法。
应用
适用性
- 主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置。
- 用于替换bean默认创建方式,例如aop通过拓展接口生成代理对应,主要用于基础框架层面。
- 在Bean实例化过程(生成实例对象)前后调用。
- postProcessBeforeInstantiation。在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例。
- 典型的例如aop返回proxy对象。
- postProcessAfterInstantiation。在目标对象被实例化之后并且在属性值被populate之前调用。
- postProcessBeforeInstantiation。在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例。
- 初始化的过程调用。
- 在初始化(setter注入,init方法)的过程中的setter注入这一步之前调用。涉及这两个方法 postProcessAfterInstantiation返回值boolean,postProcessPropertyValues()。
应用场景
实现
继承自BeanPostProcessor接口。多出了3个方法:
1 | // postProcessBeforeInstantiation方法的作用在目标对象被实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例 |
- postProcessBeforeInstantiation。
- 是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走。
- postProcessAfterInstantiation。
- 方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。如果该方法返回false,会忽略属性值的设置;如果返回true,会按照正常流程设置属性值。
- postProcessPropertyValues
- 方法对属性值进行修改(这个时候属性值还未被设置,但是我们可以修改原本该设置进去的属性值)。如果postProcessAfterInstantiation方法返回false,该方法不会被调用。可以在该方法内对属性值进行修改。
- 父接口BeanPostProcessor的2个方法postProcessBeforeInitialization和postProcessAfterInitialization都是在目标对象被实例化之后,并且属性也被设置之后调用的。
- Instantiation表示实例化,Initialization表示初始化。实例化的意思在对象还未生成,初始化的意思在对象已经生成。
postProcessBeforeInstantiation
1 | protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { |
postProcessAfterInitialization
1 | public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) |