Spring容器系列-bean的生命周期

Spring容器系列-bean的生命周期

   概要

    对于Spring Bean的生命周期来说,可以分为四个阶段,其中初始化完成之后,就代表这个Bean可以使用了,四个阶段如下:

  •    实例化 Instantiation
  •    属性赋值 Populate 
  •    初始化 Initialization
  •    销毁 Destruction

    一、Spring Bean生命周期

    1.  Spring Bean 生命周期流程图

    先来看看Spring Bean生命周期流程图如下:

    

   下面对Spring Bean生命周期进行详细描述:

    1. Bean容器在配置文件中找到Spring Bean的定义。

    2. Bean容器使用Java Reflection API创建Bean的实例。

    3. 如果声明了任何属性,声明的属性会被设置。如果属性本身是Bean,则将对其进行解析和设置。

    4. 如果Bean类实现BeanNameAware接口,则将通过传递Bean的名称来调用setBeanName()方法。

    5. 如果Bean类实现BeanClassLoaderAware接口,则将通过传递加载此Bean的ClassLoader对象的实例来调用setBeanClassLoader()方法。

    6. 如果Bean类实现BeanFactoryAware接口,则将通过传递BeanFactory对象的实例来调用setBeanFactory()方法。

    7. 如果有任何与BeanFactory关联的BeanPostProcessors对象已加载Bean,则将在设置Bean属性之前调用postProcessBeforeInitialization()方法。

    8. 如果Bean类实现了InitializingBean接口,则在设置了配置文件中定义的所有Bean属性后,将调用afterPropertiesSet()方法。

    9. 如果配置文件中的Bean定义包含init-method属性,则该属性的值将解析为Bean类中的方法名称,并将调用该方法。

   10. 如果为Bean Factory对象附加了任何Bean 后置处理器,则将调用postProcessAfterInitialization()方法。

   11. 如果Bean类实现DisposableBean接口,则当Application不再需要Bean引用时,将调用destroy()方法。

   12. 如果配置文件中的Bean定义包含destroy-method属性,那么将调用Bean类中的相应方法定义。

   说明:4、5、6步骤都可以统一看做是第三阶段初始化中的检查aware相关接口并设置相关依赖

   2.  举个例子

   1)定义一个PersonBean类,实现了DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware这4个接口,同时还有自定义的init-methoddestroy-method。这里,如果不了解这几个接口的读者,可以先去看看这几个接口的定义。

 1 public class PersonBean implements InitializingBean, BeanNameAware,BeanFactoryAware, DisposableBean {
 2     /**
 3      * 身份证号
 4      */
 5     private Integer idNo;
 6 
 7     /**
 8      * 姓名
 9      */
10     private String name;
11 
12     public PersonBean() {
13         System.out.println("1.调用构造方法:我出生了!");
14     }
15 
16     public Integer getIdNo() {
17         return idNo;
18     }
19 
20     public void setIdNo(Integer idNo) {
21         this.idNo = idNo;
22     }
23 
24     public String getName() {
25         return name;
26     }
27 
28     public void setName(String name) {
29         this.name = name;
30         System.out.println("2.设置属性:我的名字叫"+name);
31     }
32 
33     public void setBeanName(String name) {
34         System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名");
35 
36     }
37 
38     public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
39         System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了");
40     }
41 
42     public void afterPropertiesSet() throws Exception {
43         System.out.println("6.InitializingBean#afterPropertiesSet方法:入学登记");
44     }
45 
46     public void init() {
47         System.out.println("7.自定义init方法:努力上学ing");
48     }
49 
50     public void destroy() throws Exception {
51         System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
52     }
53 
54     public void destroyMethod() {
55         System.out.println("10.自定义destroy方法:睡了,别想叫醒我");
56     }
57 
58     public void work(){
59         System.out.println("Bean使用中:工作,只有对社会没有用的人才放假。。");
60     }
61 
62 }

    2)定义一个MyBeanPostProcessor实现BeanPostProcessor接口。

 1 public class MyBeanPostProcessor implements BeanPostProcessor {
 2     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 3         System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
 4         return bean;
 5     }
 6 
 7     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 8         System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
 9         return bean;
10     }
11 }

    3)配置文件spring-config.xml,指定init-methoddestroy-method属性

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean name="myBeanPostProcessor" class="ric.study.demo.ioc.life_cycle_demo_set.MyBeanPostProcessor" />
 7     <bean name="personBean" class="ric.study.demo.ioc.life_cycle_demo_set.Person"
 8           init-method="init" destroy-method="destroyMethod">
 9         <property name="name" value="Richard Yi" />
10     </bean>
11 
12 </beans>

   4)启动类,运行代码

 1     public static void main( String[] args )
 2     {
 3         ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
 4         PersonBean personBean = (PersonBean) context.getBean("personBean");
 5         personBean.work();
 6         ((ClassPathXmlApplicationContext) context).destroy();
 7 
 8     }
 9 
10 
11  //运行结果如下:
12  1.调用构造方法:我出生了!
13  2.设置属性:我的名字叫张铁钢
14  3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名
15  4.调用BeanFactoryAware#setBeanFactory方法:选好学校了
16  5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦
17  6.InitializingBean#afterPropertiesSet方法:入学登记
18  7.自定义init方法:努力上学ing
19  8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!
20  Bean使用中:工作,只有对社会没有用的人才放假。。
21  9.DisposableBean#destroy方法:平淡的一生落幕了
22  10.自定义destroy方法:睡了,别想叫醒我
23     

    二、 Spring Bean生命周期源码分析

    Bean实例化的时机也分为两种,BeanFactory管理的Bean是在使用到Bean的时候才会实例化Bean,ApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化。

    1. getBean()方法

  AbstractApplicationContext类里的refresh方法,这个方法是 ApplicationContext 容器初始化的关键点。在这个方法里,调用了finishBeanFactoryInitialization方法,这个方法里调用了getBean方法,getBean方法里调用了AbstractBeanFactorygetBean方法。
    如下图:

  

    下面我们从源码角度分析:

    Bean的创建的入口是 AbstractAutowireCapableBeanFactory#createBean()

1 /**.
2  * 这个类的核心方法,创建一个bean实例, 填充bean实例,执行后处理等
3  * @see #doCreateBean  详见doCreateBean
4  */
5 @Override
6 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
7       throws BeanCreationException {
8       //.....
9 }

  createBean最终调用的是doCreateBean方法

 1 protected Object doCreateBean(String beanName,RootBeanDefinition mbd,@Nullable Object [args) throws BeanCreationException {
 2 
 3     // 实例化bean.
 4     BeanWrapper instanceWrapper = null;
 5     if (mbd.isSingleton()) {
 6         instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
 7     }
 8     if (instanceWrapper == null) {
 9         instanceWrapper = createBeanInstance(beanName, mbd, args);
10     }
11 
12     ...
13 
14     Object exposedObject = bean;
15     try {
16 
17       //为bean设置属性值
18       populateBean(beanName,mbd,instanceWrapper);
19 
20       //初始化bean
21       exposedObject = initializeBean(beanName,exposedObject,mbd);
22     }
23     ...
24 
25     return exposedObject;
26 }

   doCreateBean方法中相关的就是populateBean方法和initializeBean方法,populateBean方法主要为bean设置属性值,我们重点关注初始化Bean方法initializeBean。

 1 protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
 2         ........
 3 
 4         try {
 5             // 执行初始化方法
 6             invokeInitMethods(beanName, wrappedBean, mbd);
 7         }
 8         ......
 9 
10         return wrappedBean;
11 }

   initializeBean中关键的是执行invokeInitMethods方法,在这里面调用afterPropertiesSet方法。

 1 protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
 2             throws Throwable {
 3         // 判断当前bean是否实现了InitializingBean接口
 4         boolean isInitializingBean = (bean instanceof InitializingBean);
 5         // 如果当前bean是一个InitializingBean
 6         if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
 7             if (logger.isTraceEnabled()) {
 8                 logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
 9             }
10             if (System.getSecurityManager() != null) {
11                 try {
12                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
13                         ((InitializingBean) bean).afterPropertiesSet();
14                         return null;
15                     }, getAccessControlContext());
16                 }
17                 catch (PrivilegedActionException pae) {
18                     throw pae.getException();
19                 }
20             }
21             else {
22                 // 真正调用当前bean的afterPropertiesSet方法
23                 ((InitializingBean) bean).afterPropertiesSet();
24             }
25         }
26 
27        if (mbd != null && bean.getClass() != NullBean.class) {
28             String initMethodName = mbd.getInitMethodName();
29             if (StringUtils.hasLength(initMethodName) &&
30                     !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
31                     !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
32                 // 最后通过反射的方式执行init-method属性对应的方法
33                 invokeCustomInitMethod(beanName, bean, mbd);
34             }
35         }
36 }

    2. afterPropertiesSet()方法

    InitializingBean只有一个方法afterPropertiesSet,见名知意,这个方法是在bean的属性值被设置以后执行。 Spring给我们提供了这么一个扩展点,可以用来做很多的事情, 比如可以修改默认设置的属性,添加补充额外的属性值或者针对关键属性做一个校验。

    Spring本身也有很多的Bean实现了InitializingBean接口, 比如Spring MVC中的RequestMappingHandlerMapping就实现了InitializingBean接口,在afterPropertiesSet中完成了一些初始化工作,比如url和controller方法的映射。

    说明:InitializingBean是Spring中很关键的一个扩展接口,其实它和bean中init-method属性对应的方法功能是一致的,都是初始化bean,我们可以二选一实现即可,当然同时使用也没有问题。init-method是通过反射实现的,性能相对差一点点。另外,如果调用afterPropertiesSet方法时出错,则不会调用init-method指定的方法。

    四、容器关闭

    1. bean的销毁入口

    如下图:

    

    2. Bean的销毁过程触发时机

    1)在Spring 中执行显式关闭上下文,关闭时候会执行实现DisposableBean接口类中的destroy() 方法    

1 ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring.xml"});
2 // 以下两种显式关闭都会调用实现了DisposableBean接口类中的destroy() 方法
3 // 1、显式关闭上下文
4 context.close();
5 // 2、显式关闭上下文
6 context.registerShutdownHook();

    2)web环境中,Tomcat关闭

    在Web环境中,当Tomcat关闭的时候就会调用到 servlet中的销毁方法,在这个方法中就会最终也会掉用到Spring中 DisposableBeanAdapter类的destroy()方法,该方法就会根据前面的收集进行调用。

 1 public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
 2     public ContextLoaderListener() {
 3     }
 4 
 5     public ContextLoaderListener(WebApplicationContext context) {
 6         super(context);
 7     }
 8 
 9     public void contextInitialized(ServletContextEvent event) {
10         this.initWebApplicationContext(event.getServletContext());
11     }
12     //  servlet中的销毁方法
13     public void contextDestroyed(ServletContextEvent event) {
14        // 该方法最终会调用Spring的DisposableBeanAdapter类的destroy()
15         this.closeWebApplicationContext(event.getServletContext());
16         ContextCleanupListener.cleanupAttributes(event.getServletContext());
17     }

    servlet中的closeWebApplicationContext方法最终也会掉用到Spring中的DisposableBeanAdapter类的destroy()方法,该方法就会根据前面的收集进行调用。 

    3. 销毁过程解析

   与Bean初始化类似,当容器关闭时,可以看到对Bean销毁方法的调用。销毁过程是这样的。顺着close()-> doClose() -> destroyBeans() -> destroySingletons() -> destroySingleton() -> destroyBean() -> bean.destroy() ,会看到最终调用Bean的销毁方法。

    AbstractApplicationContext#close(),其实实现的是ConfigurableApplicationContext的close()方法

 1     @Override
 2     public void close() {
 3         synchronized (this.startupShutdownMonitor) {
 4             doClose();
 5             // If we registered a JVM shutdown hook, we don't need it anymore now:
 6             // We've already explicitly closed the context.
 7             if (this.shutdownHook != null) {
 8                 try {
 9                     Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
10                 }
11                 catch (IllegalStateException ex) {
12                     // ignore - VM is already shutting down
13                 }
14             }
15         }
16     }

    DefaultSingletonBeanRegistry#destroyBean()源码如下:  

 1 protected void destroyBean(String beanName, DisposableBean bean) {
 2         // 忽略
 3 
 4         // Actually destroy the bean now...
 5         if (bean != null) {
 6             try {
 7                 bean.destroy();
 8             }
 9             catch (Throwable ex) {
10                 logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
11             }
12         }
13 
14         // 忽略
15

   这里注意哦,这个Bean的类型实际上是DisposableBeanAdapter。

   3. DisposableBeanAdapter

  DisposableBeanAdapter是用来管理Spring Bean的销毁的,实际上这里运用了适配器模式。负责将各种销毁方式适配为统一的 destroy() 方法,以便 Spring 容器在关闭时一致地调用。

   AbstractAutowireCapableBeanFactory#doCreateBean

 1 try {
 2     ... 
 3     
 4     //注册bean销毁时的类DisposableBeanAdapter:如果实现了 DisposableBean接口或者提供了destroy—method 在这里注册
 5     registerDisposableBeanIfNecessary(beanName, bean, mbd);
 6 }
 7 catch (BeanDefinitionValidationException ex) {
 8     throw new BeanCreationException(
 9         mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
10 }

   源码位置:AbstractBeanFactory#registerDisposableBeanIfNecessary()

 1 protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
 2         AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
 3         if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
 4             if (mbd.isSingleton()) {
 5                 // 注册一个DisposableBean实现,该实现将执行给定bean的所有销毁工作。
 6                 // 包括:DestructionAwareBeanPostProcessors,DisposableBean接口,自定义destroy方法。
 7                 registerDisposableBean(beanName,
 8                         new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
 9             }
10             else {
11                 // A bean with a custom scope...
12                 Scope scope = this.scopes.get(mbd.getScope());
13                 if (scope == null) {
14                     throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
15                 }
16                 scope.registerDestructionCallback(beanName,
17                         new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
18             }
19         }
20     }

  再来看看destroy()的具体方法。

  DisposableBeanAdapter#destroy()源码如下:

 1 @Override
 2     public void destroy() {
 3         if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
 4             for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
 5                 processor.postProcessBeforeDestruction(this.bean, this.beanName);
 6             }
 7         }
 8 
 9         if (this.invokeDisposableBean) {
10             if (logger.isDebugEnabled()) {
11                 logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
12             }
13             try {
14                 if (System.getSecurityManager() != null) {
15                     AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
16                         @Override
17                         public Object run() throws Exception {
18                             ((DisposableBean) bean).destroy();
19                             return null;
20                         }
21                     }, acc);
22                 }
23                 else {
24                     // 调用 DisposableBean 的 destroy()方法
25                     ((DisposableBean) bean).destroy();
26                 }
27             }
28             catch (Throwable ex) {
29                 String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
30                 if (logger.isDebugEnabled()) {
31                     logger.warn(msg, ex);
32                 }
33                 else {
34                     logger.warn(msg + ": " + ex);
35                 }
36             }
37         }
38 
39         if (this.destroyMethod != null) {
40             // 调用 设置的destroyMethod
41             invokeCustomDestroyMethod(this.destroyMethod);
42         }
43         else if (this.destroyMethodName != null) {
44             Method methodToCall = determineDestroyMethod();
45             if (methodToCall != null) {
46                 invokeCustomDestroyMethod(methodToCall);
47             }
48         }
49     }

   参考链接:

   https://segmentfault.com/a/1190000020747302

   https://juejin.cn/post/7075168883744718856

   https://yangzhiwen911.github.io/

 

posted @ 2024-06-17 21:26  欢乐豆123  阅读(17)  评论(0编辑  收藏  举报