1、Spring Bean 生命周期整体流程
Spring Bean 的生命周期,概括起来主要有四个阶段:
- 实例化(Instantiation):基于
BeanDefinition
实例化一个 Bean ,此时 Bean 里的属性字段为 null 尚未赋值; - 属性赋值(Populate):为 Bean 里的属性字段赋值,包括解析
@Autowired
等注入的属性字段; - 初始化(Initialization):将属性赋值后的 Bean 注册到 Spring 容器中,这里 Spring 提供了很多拓展接口,支持用户自定义 Bean 的初始化过程中做哪些操作,具体接口在第3小节介绍;
- 销毁(Destruction):销毁 Bean ,通常在 Spring 应用结束时调用。
2、面试说辞
Bean 生命周期的整个执行过程描述如下:
- Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
- 对 Bean 进行属性注入。
- 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
- 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
- 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
- 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作。
- 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
- 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization(),此处非常重要,Spring 的 AOP 就是利用它实现的。
- 此时,Bean 已经可以被应用系统使用了。
- 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean。
- 如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
3、常用接口方法说明
对于上面的接口和方法,可以分为以下三大类:
- Bean自身的方法:比如构造函数、getter/setter 以及 Bean 中自定义的初始化方法(
init-method
)和销毁方法(destory-method
)等; - Bean直接实现的接口的方法:比如
BeanNameAware
、BeanFactoryAware
、InitializingBean
、DisposableBean
等方法,这些方法只对当前 Bean 生效; - 容器级生命周期的方法:这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中,在 Spring 容器创建任何 Bean 的时候,这些接口对应的方法都会发生作用。比如
InstantiationAwareBeanPostProcessor
、BeanPostProcessor
接口里的方法。Bean自身的方法和Bean直接实现的接口的方法都只对当前Bean起作用,但是容器级生命周期方法是对所有的bean都起作用的。
3.1 Bean自身的方法
Bean 的构造方法和 getter/setter 方法就不在介绍了,主要介绍一下通过@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
注解实现的初始化和销毁方法。
3.1.1 @Bean(initMethod=”initMethod”, destroyMethod=”destroyMethod”)
Spring 支持用户在 Bean 中自定义 Bean 的初始化和销毁时做的动作,即用户在 Bean 类中声明 public 的初始化和销毁方法,在公共的 BeanConfig 里通过@Bean
注解创建这个 Bean 时,在@Bean
注解里通过initMethod
和destroyMethod
参数将 Bean 类中自定义的初始化方法名和销毁方法名传入。
需要注意的是:
@Bean
注解里initMethod
参数指定的初始化 Bean 的方法,是在InitializingBean
接口的afterPropertiesSet()
方法执行之后再执行的;@Bean
注解里destroyMethod
参数指定的销毁 Bean 的方法,是在DisposableBean
接口的destroy()
方法执行之后再实行的。
@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
的Demo如下:
@Configuration
@ComponentScans(value= {@ComponentScan("com.jerry.springbootstudy.lifecycle")})
public class LifeCycleConfig {
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user()
{
return new User();
}
}
@Component
public class User implements {
public void initMethod() {
System.out.println("调用自定义的init方法初始化方法");
}
public void destroyMethod() {
System.out.println("调用自定义的destroy方法销毁对象");
}
}
3.2 Bean直接实现的接口的方法
3.2.1 BeanNameAware接口
BeanNameAware
接口是个函数式接口,只有一个方法void setBeanName(String s)
,通过该接口可以获取 Bean 的 name,demo 如下:
@Service
public class BizService implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String s) {
this.beanName = s;
}
public String getBeanName() {
return beanName;
}
}
BizService 实例可以通过调用getBeanName()
方法获取当前 BizService 实例(Bean)的 beanName。
3.2.2 BeanFactoryAware接口
BeanFactoryAware
接口也是一个函数式接口,只有一个方法void setBeanFactory(BeanFactory beanFactory) throws BeansException
,通过该接口可以获取当前 Bean 对应的 beanFactory 的引用,demo 如下:
@Service
public class BizService implements BeanFactoryAware {
private BeanFactory factory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
public BeanFactory getFactory() {
return factory;
}
}
BizService 实例可以通过调用getFactory()
方法获取当前 BizService 实例(Bean)对应的 beanFactory 的引用。
这里简要介绍一下 Spring 的 BeanFactory :Spring 提供了两种类型的IOC容器:BeanFactory 接口和 ApplicationContext 接口,二者的关系是 BeanFactory 接口是 ApplicationContext 接口的父父接口;
二者的区别在于:
- BeanFactory 是基础类型的Ioc容器,默认采取延迟初始化(懒加载)策略,所谓懒加载是指在容器初始化时并没有初始化容器中的bean,只有当程序需要访问其中一个被容器管理的对象时才会对bean进行初始化及其注入操作;
- ApplicationContext 在 BeanFactory 基础上构建,除了拥有 BeanFactory 的所有特性,它还支持事件发布、国际化信息的支持等。另外,ApplicationContext 管理的对象会在容器初始化的时候将其全部初始化。
3.2.3 ApplicationContextAware接口
ApplicationContextAware
接口也是一个函数式接口,只有一个方法:void setApplicationContext(ApplicationContext applicationContext)throws BeansException
,通过该接口可以获取 Spring 的上下文ApplicationContext 实例。Demo 如下: ```java
public class ApplicationContextUtil implements ApplicationContextAware{
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的回调方法,设置上下文环境
*
* @param applicationContext spring上下文对象
* @throws BeansException 抛出spring异常
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
/**
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取对象
*
* @param name spring配置文件中配置的bean名或注解的名称
* @return 一个以所给名字注册的bean的实例
* @throws BeansException 抛出spring异常
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T) applicationContext.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clazz 需要获取的bean的类型
* @return 该类型的一个在ioc容器中的bean
* @throws BeansException 抛出spring异常
*/
public static <T> T getBean(Class<T> clazz) throws BeansException {
return applicationContext.getBean(clazz);
}
/**
* 如果ioc容器中包含一个与所给名称匹配的bean定义,则返回true否则返回false
*
* @param name ioc容器中注册的bean名称
* @return 存在返回true否则返回false
*/
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
}
获取到了 Spring 的上下文,意味着你可以通过实现类的实例获取 Spring 中的所有 Bean,或者判断当前上下文中是否含有指定名称的 Bean,但貌似实际项目中也没用到过。
<a name="z8YGz"></a>
### 3.2.4 InitializingBean接口
`InitializingBean`接口也是一个函数式接口,只有一个方法:`void afterPropertiesSet() throws Exception`,通过该接口可以自定义 Bean 初始化时执行的操作。使用场景比如某功能需要在 Spring 启动(对应的就是你这个服务启动)的时候就执行,而不是通过自动注入 Bean 然后调用 Bean 的方法去触发,这个时候就可以将执行操作写在`afterPropertiesSet`方法里,IOC容器选择 ApplicationContext ,在 Spring 容器初始化的时候就初始化 Bean ,进而调用`afterPropertiesSet`方法里要执行的操作。<br />`InitializingBean`接口的简单demo如下:
```java
@Service
public class TestInitializingBean implements InitializingBean{
private Logger logger = LoggerFactory.getLogger(TestInitializingBean.class);
@Override
public void afterPropertiesSet() throws Exception {
logger.info("开始工作......");
logger.info("结束工作......");
}
}
这里说一下3.1.1中@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
中指定的初始化方法和InitializingBean
接口的afterPropertiesSet
方法的异同点:
- 相同点:功能都是 Bean 初始化时指定要执行的操作;
- 不同点:
@Bean
注解中的init-method
参数是通过反射执行的,而afterPropertiesSet
是直接执行的,因此afterPropertiesSet
的效率要高一些;- 执行顺序上
**afterPropertiesSet**
先执行,**@Bean**
注解中的**init-method**
后执行。3.2.5 DisposableBean接口
DisposableBean
接口也是一个函数式接口,只有一个方法void destroy()
,在容器销毁的时候调用,且DisposableBean
接口定义的destroy
方法先执行,@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
中指定的销毁方法后执行。
3.3 容器级生命周期方法
3.3.1 BeanPostProcessor接口
该接口也叫后置处理器,作用是在 Bean 对象在实例化和依赖注入完毕后,在显示调用初始化方法(InitializingBean
接口的afterPropertiesSet
方法或自定义的init
方法)的前后添加我们自己的逻辑。接口中有两个方法,如下:
public interface BeanPostProcessor {
//实例化、依赖注入完毕,在调用显式的初始化之前完成一些定制的初始化任务。
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//实例化、依赖注入、初始化完毕时执行定制的任务。
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
postProcessBeforeInitialization
在InitializingBean
接口的afterPropertiesSet
方法或自定义的init-method
方法执行前调用,postProcessAfterInitialization
在InitializingBean
接口的afterPropertiesSet
方法或自定义的init-method
方法执行后调用。
3.3.2 InstantiationAwareBeanPostProcessor接口
InstantiationAwareBeanPostProcessor
接口是BeanPostProcessor
的子接口,该接口跟 Bean 的实例化过程有关,接口中有3个方法(不包含父接口BeanPostProcessor
里的2个方法),如下:
package org.springframework.beans.factory.config;
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
}
postProcessBeforeInstantiation |
该方法在目标对象实例化之前调用,该方法的返回值类型是Object。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法返回为null,后续的方法都正常执行,如果该方法返回实例对象,则后续方法都不执行,直接执行BeanPostProcessor 接口的postProcessAfterInitialization 方法。 |
---|---|
postProcessAfterInstantiation |
在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是 null。该方法的返回值是决定要不要调用postProcessPropertyValues 方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck() ),如果该方法返回 false,并且不需要 check,那么postProcessPropertyValues 就会被忽略不执行;如果返回 true,postProcessProperties 方法就会被执行。 |
postProcessProperties |
如果postProcessAfterInstantiation 方法返回 false,该方法可能不会被调用。此时属性值还未被设置,但是可以通过postProcessProperties 方法修改原本应该设置进去的属性值。 |
注意区分两个单词:
单词 | 含义 | 对应接口 |
---|---|---|
Instantiation | 表示实例化,对象还未生成 | InstantiationAwareBeanPostProcessor接口 |
Initialization | 表示初始化,对象已经生成 | BeanPostProcessor接口 |
3.3.3 BeanFactoryPostProcessor接口
BeanFactoryPostProcessor
接口也是一个函数式接口,只有一个方法postProcessBeanFactory
,如下:
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
实现该接口,可以对beanDifinition
进行修改。也就是说,Spring 允许BeanFactoryPostProcessor
在容器实例化任何 Bean 之前读取配置元数据(beanDifinition),并可以根据需要进行修改,例如可以把 Bean 的 scope 从 singleton 改为 prototype,也可以把 property 的值给修改掉。可以同时配置多个BeanFactoryPostProcessor
,并通过设置 order 属性来控制各个BeanFactoryPostProcessor
的执行次序。注意:**BeanFactoryPostProcessor**
的**postProcessBeanFactory**
方法的执行顺序在**InstantiationAwareBeanPostProcessor**
接口的**postProcessBeforeInstantiation**
方法执行之前。
4、Spring生命周期Demo
根据第一节介绍的 Spring 生命周期详细的过程,设计 Demo 验证整个流程。
User:
@Component
public class User implements InitializingBean, BeanNameAware, DisposableBean {
@Value("001")
private String id;
@Value("Jerry")
private String name;
public User() {
System.out.println("3、调用构造函数");
}
public void initMethod() {
System.out.println("9、调用自定义的init方法初始化方法");
}
public void destroyMethod() {
System.out.println("12、调用自定义的destroy方法销毁对象");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
System.out.println("5、属性注入(id)");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("5、属性注入(name)");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("8、调用InitializingBean实例的afterPropertiesSet");
}
@Override
public void setBeanName(String name) {
System.out.println("6、调用BeanNameAware接口的setBeanName方法");
}
@Override
public void destroy() throws Exception {
System.out.println("11、调用DisposableBean接口的destroy方法");
}
}
User类中主要定义了 Bean 自身的方法和 Bean 实现的接口。
MyBeanFactoryPostProcessor:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("1、调用BeanFactoryPostProcessor接口的postProcessBeanFactory方法,beanFactory: " + beanFactory);
}
}
MyBeanPostProcessor:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("7、调用BeanPostProcessor接口的postProcessBeforeInitialization方法, bean:" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
User user = (User) bean;
System.out.println("10、调用BeanPostProcessor接口的postProcessAfterInitialization方法, user id is: " + user.getId() + ", user name is: " + user.getName());
return bean;
}
}
MyInstantiationAwareBeanPostProcessor:
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("2、调用InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法");
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("4、调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation方法");
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("5、调用InstantiationAwareBeanPostProcessor接口的postProcessProperties方法");
return pvs;
}
}
MyBeanFactoryPostProcessor
、MyBeanPostProcessor
和MyInstantiationAwareBeanPostProcessor
是 Spring 容器级方法对应的接口的实现类。
LifeCycleConfig:
@Configuration
@ComponentScans(value= {@ComponentScan("com.jerry.springbootstudy.lifecycle")})
public class LifeCycleConfig {
@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user()
{
return new User();
}
@Bean
public MyBeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
@Bean
public MyBeanFactoryPostProcessor myBeanFactoryPostProcessor() {
return new MyBeanFactoryPostProcessor();
}
@Bean
public MyInstantiationAwareBeanPostProcessor myInstantiationAwareBeanPostProcessor() {
return new MyInstantiationAwareBeanPostProcessor();
}
}
配置类,网上很多例子都是基于 Spring xml 配置文件的,但是现在开发项目中应该基本没有再用xml配置文件的了,都是用的 Config 类导入,这里 LifeCycleConfig 相当于一个主配置类(BeanConfig)。
LifeCycleConfigTest:
public class LifeCycleConfigTest {
public static void main(String[] args) {
// 基于配置类创建容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
// 从容器中获取bean
Object user = ctx.getBean("user");
// 销毁容器
ctx.close();
}
}
注意这里要写成 main 函数启动的方式,而不是之前那种 Spirng 程序的启动方式,测试代码中通过AnnotationConfigApplicationContext
生成 Spring 基于注解的上下文,再从上下文中获取 Bean,完成 user Bean 的初始化,并通过打印消息的方式代表对应的初始化方法已经执行了。为了打印结果对齐,没有采用log打印日志,而是通过System.out.println
的方式将日志打印到控制台,启动 Spring 应用后,控制台的打印结果如下: