1. 前言
最初接触java的接口服务是servlet,最基础要掌握的就是servlet的生命周期。现在都是基于springboot开发后台接口,spring框架的复杂度远远高于原始的servlet,弄清楚spring中bean的生命周期,有利于我们对其框架的熟练掌握。
1.1、servlet生命周期
我们先回顾一下servlet的生命周期吧。
construct
和populate
:Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法)。init
:在servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。service
:它是servlet的核心,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。destroy
:当容器检测到一个servlet实例应该从服务中被移除的时候,容器就会调用实例的destroy()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中。当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。对于每一个Servlet实例,destroy()方法也只被调用一次。
2. spring容器加载
springboot的启动类中,都会执行SpringApplication.run
方法。在创建上下文环境之后,最核心的方法就是refreshContext
,对上下文环境进行初始化操作,本段就着重说明这一过程。
2.1. refreshContext全过程
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 准备环境
* 1. 记录容器的启动时间startupDate,标记容器为激活
* 2. 初始化上下文环境如文件路径信息
* 3. 验证必填属性是否填写
*/
prepareRefresh();
/**
* 告诉子类去刷新beanFactory
* 这步完成后配置文件就解析成一个个beanDefination,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 设置beanFactory类加载器,添加多个beanPostProcesser
*/
prepareBeanFactory(beanFactory);
try {
/**
* 允许子类上下文中对beanFactory做后期处理
*/
postProcessBeanFactory(beanFactory);
/**
* 执行 context 中注册的 BeanFactoryPostProcessor中的postProcessBeanFactory() 方法
* 1. BeanFactoryPostProcessor 是 bean 属性处理容器。即管理 bean工厂中的BeanDefinition
* 2. BeanDefinition在 spring mvc中就是xml文件中对应的bean标签(注意,是标签,而不是真实的 java bean)
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 注册 BeanPostProcessor 的实现类
* 1. BeanPostProcessor接口包含两个方法:postProcessBeforeInitialization 和 postProcessAfterInitialization
* 2. 这里只是注册,两个方法实际分别在 Bean初始化之前和初始化之后得到执行
*/
registerBeanPostProcessors(beanFactory);
/**
* 初始化ApplicationContext的MessageSource,MessageSource是国际化相关的接口
*/
initMessageSource();
/**
* 初始化ApplicationContext事件广播器
*/
initApplicationEventMulticaster();
// 初始化子类特殊bean(钩子方法)
onRefresh();
/**
* 注册事件监听器
*/
registerListeners();
/**
* 初始化所有的单例java bean(不包含懒加载的类)
*/
finishBeanFactoryInitialization(beanFactory);
/**
* 广播事件,ApplicationContext初始化完成
*/
finishRefresh();
} catch (BeansException ex) {
....................
}
}
}
2.2. BeanDefinition
BeanDefinition 是 spring bean 的建模对象,即把一个bean实例化出来的模型对象。有人会问把一个bean实例化出来有Class就行了啊,Class也就是我们通常说的类对象,就是一个普通对象的建模对象,那么为什么spring不能用Class来建立bean呢?很简单,因为Class无法完成bean的抽象,比如bean的作用域,bean的注入模型,bean是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个BeanDefinition类来抽象这些信息,以便于spring能够完美的实例化一个bean。
spring扫描到某个bean的类时,除了要存储类名、构造器等Class的基础信息以外,还要存储scope,lazy,dependsOn
等spring的属性。BeanDefintion是一个类,专门用来存储上述bean中的各种属性。因此当spring扫描到一个符合规则的类时,首先是实例化一个BeanDefinition的对象,并且把根据类的类名生成一个bean的名字,继而以beanName为key,BeanDefinition对象为value,存放在一个map中。
值得注意的是,这里都是讲在扫描bean后,BeanDefinition的实例化,以及放入BeanDefinition的map中。还没有讲到Bean的实例化和装载。
BeanDefinition 的属性方法如下:
- String:
getBeanClassName
: 返回当前bean definition定义的类名 - ConstructorArgumentValues:
getConstructorArgumentValues
:返回bean的构造函数参数 - String[]:
getDependsOn
:返回当前bean所依赖的其他bean的名称 - String:
getFactoryBeanName
: 返回factory bean的名称 - String:
getFactoryMethodName
: 返回工厂方法的名称 - BeanDefinition:
getOriginatingBeanDefinition
: 返回原始的BeanDefinition,如果不存在返回null - String:
getParentName
: 返回当前bean definition的父definition的名字 - MutablePropertyValues:
getPropertyValues
: 返回一个用于新的bean实例上的属性值 - String:
getScope
: 返回当前bean的目标范围 - boolean:
isAbstract
: 当前bean是否是abstract,意味着不能被实例化 - boolean:
isLazyInit
: bean是否是延迟初始化 - boolean:
isPrimary
: bean是否为自动装配的主要候选bean - boolean:
isPrototype
: bean是否是多实例 - boolean:
isSingleton
: bean是否是单例 - void:
setAutowiredCandidate
(boolean): 设置bean是否对其他bean是自动装配的候选bean - void:
setBeanClassName
(String): 指定bean definition的类名 - void:
setDependsOn
(String ...): 设置当前bean初始化所依赖的beans的名称 - void:
setFactoryBeanName
(String): 如果factory bean的名称 - void:
setFactoryMethodName
(String): 设置工厂的方法名 - void:
setLazyInit
(boolean lazyInit): 设置是否延迟初始化 - void:
setParentName
(String): 设置父definition的名称 - void:
setPrimary
(boolean): 设置是否主要的候选bean - void:
setScope
(String): 设置bean的范围,如:单例,多实例
2.3. BeanFactoryPostProcessor
BeanFactoryPostProcessor是实现spring容器功能扩展的重要接口,例如修改BeanDefinition,实现bean动态代理等。很多框架都是通过此接口实现对spring容器的扩展,例如mybatis与spring集成时,只定义了mapper接口,无实现类,接口也没有添加@Component等注解,但spring却可以完成自动注入,也是基于BeanFactoryPostProcessor接口来实现的。
BeanFactoryPostProcessor.java
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
前文说到BeanDefinition在实例化之后,被放入BeanDefinition map中,而BeanFactoryPostProcessor就是在BeanDefinition map初始化完成后,Bean实例构造方法执行之前
执行的。
BeanFactoryPostProcessor的主要作用是,开发者可以修改容器中所有BeanDefinition的信息
,接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。但 绝对不允许进行bean实例化相关的操作!
因为中spring加载机制中,BeanFactoryPostProcessor是在Bean构造方法之前执行的,如果这个时候提前实例化bean,很可能会一连串的问题。
如:在接口用通过configurableListableBeanFactory.getBean(“user”),获取User类的bean,实际上即实例化了User的bean。会导致在后续其他节点代码中,无法注入User的bean实例。
2.4. BeanPostProcessor
如果说BeanFactoryPostProcessor是对BeanDefinition的扩展处理,那么BeanPostProcessor更多的是对Bean的扩展处理。BeanPostProcessor的触发时间是在 Bean的实例化之后(执行了构造方法,并且对属性进行了赋值),在Bean的init方法执行之前(@PostConstruct注解方法、InitializeBean接口方法、@Bean中initMethod方法)
。
BeanPostProcessor.java
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
BeanPostProcessor接口中定义两个方法,根据名字可以推测,postProcessBeforeInitialization在Bean的init方法之前执行,postProcessAfterInitialization在Bean的init方法之后执行。这点很好的体现了spring的AOP思想,可以理解为BeanPostProcessor是对Bean的init方法执行前后,做了一层切面拦截。
有一个实现Spring aop 的BeanPostProcessor 叫 AnnotationAwareAspectJAutoProxyCreator。在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,对对象进行判断,看他需不需要织入切面逻辑,如果需要,那我就根据这个对象,生成一个代理对象,然后返回这个代理对象,那么最终注入容器的,自然就是代理对象了。
2.5. finishBeanFactoryInitialization
该方法会实例化所有剩余的非懒加载单例 bean。值得注意的是“所有剩余的”
,就是有一些特殊的bean,这该方法之前就已经加载了,如:
- 一些spring容器内部的bean;
- 实现了 BeanFactoryPostProcessor 接口的bean;
- 实现了 BeanPostProcessor 接口的 bean(但是这些bean会在这个方法中触发)。
其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 的触发也是在这个方法中。
3. Bean的生命履历
基于spring的refresh过程,我们把关于bean的部分拎出来,就形成了下面的bean的加载过程:
- spring基于@ComponentScan等注解扫描到bean,并将每个bean实例化成包含bean各种属性的
BeanDefinition
的对象,放入beanDefinitionMap中。 - spring 中的 bean 简单分为:特殊bean和普通bean,beanFactory实际会先实例化特殊bean。
- 实现
BeanFactoryPostProcessor
接口的就是一种特殊bean,在beanDefinitionMap加载后触发,可以自定义实现类,对其中的BeanDefinition进行修改。 construct
过程。BeanFactory 执行getBean方法生产其他的普通bean(调用类的构造方法,或FactoryBean的getObject方法,以及@Bean注解的方法)。- 此时bean获取会受三级缓存(singletonFactories、earlySingletonObjects、singletonObjects)影响,如earlySingletonObjects会提前曝光尚未populate属性数据的单例对象,可解决循环依赖问题。
populate
过程。设置bean的依赖关系(基于属性注入)以及属性值。- 对于实现
BeanPostProcessor
接口的类,执行接口中的postProcessBeforeInitialization 方法。 initialze
过程。执行bean的各种初始化方法,initialze方法的优先级如下:(1)@PostConstruct指定的方法 -> (2)InitializingBean接口的afterPropertiesSet()方法 -> (3)@Bean中initMethod指定的方法
。- 对于实现
BeanPostProcessor
接口的类,执行接口中的postProcessAfterInitialization 方法。 destroy
过程,容器销毁,执行bean的各种销毁方法,destroy方法的优先级如下:(1)@PostDestroy指定的方法 -> (2)DisposableBean接口的destroy()方法 -> (3)@Bean中destroyMethod指定的方法
。
在spring bean生命周期中,网上有人用Bean的“初始化”、“实例化”等名字,很容易搞混,所以我使用了下列几个英文单词,感觉更能表达这些过程:
- construct:是对象创建的过程。比如使用构造方法new对象。
- populate:是为对象中的属性赋值的过程,包括@Autowired等依赖注入,@Value的赋值等。
- initialize:执行初始化方法。
其实并非只有在populate
时才会注入依赖,还是要取决于属性的注入方式:基于属性注入、setter注入和构造器注入等。如果是基于构造器注入,在construct
过程中,就会导入依赖。
另外我们中使用@Value、@ConfigurationProperties等注解赋值,也是属于populate
过程的操作。
4、bean相关方法
对于上述关于bean生命周期的介绍,我们写个demo,一一实现相关的方法,亲眼看看他们的加载过程。
4.1、BeanFactoryPostProcessor
@Slf4j
@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
/**
* getBean 是获取Object对象,getBeanDefinition 是获取Class类
* 可以修改BeanDefinition
* 但getBean方法不要用,会导致提前加载bean,bean实例化紊乱
* @param beanFactory
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
log.warn("实现 BeanFactoryPostProcessor 接口:开始了!");
Arrays.stream(beanFactory.getBeanDefinitionNames())
.filter(beanName -> User.USER_BEAN_NAME.equals(beanName))
.forEach(beanName -> {
//BeanDefinition beanDefinition=beanFactory.getBeanDefinition(beanName);
//beanDefinition.getPropertyValues().add("name","Kerry");
log.warn("BeanFactoryPostProcessor:Bean name is " + beanName);
});
}
}
主要是对 BeanDefinitionName 的修改,这里将对象的属性name的值设置为Kerry。
4.2、BeanPostProcessor
@Slf4j
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {
/**
* Bean 初始化方法 执行前 调用
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(User.USER_BEAN_NAME.equals(beanName)) {
log.warn("BeanPostProcessor-name:"+((User)bean).getName());
log.warn("实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=" + beanName);
}
return bean;
}
/**
* Bean 初始化方法 执行后 调用
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(User.USER_BEAN_NAME.equals(beanName)) {
log.warn("实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=" + beanName);
}
return bean;
}
}
4.3、初始化和销毁方法
初始化和销毁两种方法往往是配套的,有下面三种,并且按照下列顺序执行:
- @PostConstruct 和 @PreDestroy 注解修饰的方法,分别在初始化和销毁时执行。
- 实现 InitializeBean 接口的 afterPropertiesSet()方法,和实现 DisposableBean 接口的 destroy()方法,分别在初始化和销毁时执行。
- @Bean 中initMethod属性指定的方法,和destroyMethod属性指定的方法,分别在初始化和销毁时执行。
我们在User类中定义了这些初始化和销毁方法,由于还要测试依赖注入顺序,也定义了Type类的方法。
User.java
@Slf4j
public class User implements InitializingBean, DisposableBean {
public final static String USER_BEAN_NAME="user";
//配置文件中,注入的值为 demoName
@Value("${params.user.name}")
private String name;
private Integer age=1;
@Autowired
private Type type;
@Override
public String toString(){
return "My name is "+name+",I'm "+age+"years old.";
}
/**
* 构造方法
* 1、默认构造方法
* 2、重载构造方法
*/
public User(){
log.warn("构造方法:public User()");
}
/**
* 初始化
* 1、注解:@PostConstruct 的方法
* 2、实现 InitializeBean 接口:afterPropertiesSet()
* 3、@Bean 中指定的 initMethod
*/
@PostConstruct
public void postConstruct(){
log.warn("注解:@PostConstruct 的方法");
}
@Override
public void afterPropertiesSet(){
log.warn("实现 InitializeBean 接口:afterPropertiesSet()");
}
public void initMethod(){
log.warn("@Bean 中指定的 initMethod ");
}
/**
* 销毁
* 1、注解:@PreDestroy 的方法
* 2、实现 DisposableBean 接口:destroy()
* 3、@Bean 中指定的 destroyMethod
*/
@PreDestroy
public void preDestroy(){
log.warn("注解:@PreDestroy 的方法");
}
@Override
public void destroy(){
log.warn("实现 DisposableBean 接口:destroy()");
}
public void destroyMethod(){
log.warn("@Bean 中指定的 destroyMethod");
}
public void setName(String name) {
log.warn("User.setName方法:"+name);
this.name = name;
}
}
Type.java
@Component
@Data
@Slf4j
public class Type implements InitializingBean {
private String typeCode;
public Type(){
log.warn("构造方法:public Type()");
}
@Override
public void afterPropertiesSet(){
log.warn("Type:实现 InitializeBean 接口:afterPropertiesSet()");
}
}
BeanConfig.java
@Configuration
public class BeanConfig {
@Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")
public User user(){
return new User();
}
}
4.4. 结果验证
还记得我们在BeanFactoryPostProcessorImpl中修改了name的值吗?我们先把修改的代码注释掉,执行之后的日志如下,的确按照前面的Bean生命周期加载的:
p.k.e.s.c.BeanFactoryPostProcessorImpl : 实现 BeanFactoryPostProcessor 接口:开始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User : 构造方法:public User()
p.k.exercise.springexercise.pojo.Type : 构造方法:public Type()
p.k.exercise.springexercise.pojo.Type : Type:实现 InitializeBean 接口:afterPropertiesSet()
p.k.e.s.config.BeanPostProcessorImpl : BeanPostProcessor-name:demoName
p.k.e.s.config.BeanPostProcessorImpl : 实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User : 注解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User : 实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User : @Bean 中指定的 initMethod
p.k.e.s.config.BeanPostProcessorImpl : 实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User : 注解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User : 实现 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User : @Bean 中指定的 destroyMethod
现在把BeanFactoryPostProcessorImpl中的注释放开,日志变了:
p.k.e.s.c.BeanFactoryPostProcessorImpl : 实现 BeanFactoryPostProcessor 接口:开始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User : 构造方法:public User()
p.k.exercise.springexercise.pojo.Type : 构造方法:public Type()
p.k.exercise.springexercise.pojo.Type : Type:实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User : User.setName方法:Kerry
p.k.e.s.config.BeanPostProcessorImpl : BeanPostProcessor-name:Kerry
p.k.e.s.config.BeanPostProcessorImpl : 实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User : 注解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User : 实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User : @Bean 中指定的 initMethod
p.k.e.s.config.BeanPostProcessorImpl : 实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User : 注解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User : 实现 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User : @Bean 中指定的 destroyMethod
可以看到如果我们在BeanFactoryPostProcessor接口中通过BeanDefinition 的PropertyValue 设置属性值,其实只是修改了该属性的setter方法,而真正执行该方法,还是在populate
阶段,而非BeanFactoryPostProcessor执行阶段。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。