spring扩展接口解析1--InitializingBean接口和Disposable接口

前言

Spring框架之所以强大,其中有一个核心功能就是提供了扩展支持,Spring容器虽然管理了所有的Spring Bean,单例的bean初始化之后就会放入Spring容器,在整个生命周期内都不可变。但是在实际业务场景中,有时我们需要对bean有额外的扩展功能。

此时就可以用到Spring提供的众多扩展接口,本文就盘点下Spring框架中常用的扩展接口中的两个 InitializingBean和DisposableBean接口

一、InitializingBean 和 DisposableBean 接口

InitializingBean和DisposableBean都是一个接口,从名字可以看出分别是bean初始化和bean销毁相关的接口

InitialzingBean和DisposableBean源码分别如下:

1 public interface InitializingBean {
2 
3     /**
4      * 初始化方法,在bean的属性设置完成后执行
5      */
6     void afterPropertiesSet() throws Exception;
7 
8 }
1 public interface DisposableBean {
2 
3     /**
4      * 销毁方法,在bean准备销毁的时候执行
5      */
6     void destroy() throws Exception;
7 
8 }

这两个接口分别都只有一个方法需要实现,一个是初始化方法afterPropertiesSet()和销毁方法destroy()

如果bean实现了InitialzingBean接口,就需要实现afterPropertiesSet方法,当bean初始化完成就会自动调用该方法,做一些bean初始化之后的额外工作,另外除了该方法,配置bean的时候还可以配置bean的初始化方法,如配置init-method,这个同样是当bean初始化之后需要执行的初始化方法,而afterPropertiesSet方法是在init-method方法之前执行的。所以可以总结一个bean从创建到初始化完成的全部过程如下:

1、通过构造器创建bean

2、属性注入

3、执行afterPropertiesSet方法

4、执行init-method方法

使用案例如下:

 1 public class MyBean implements InitializingBean{
 2 
 3     private UserService userService;
 4 
 5     public MyBean(){
 6         System.out.println("执行构造器方法");
 7     }
 8 
 9     public void initMethod(){
10         System.out.println("执行配置的初始化方法");
11     }
12 
13     @Override
14     public void afterPropertiesSet() throws Exception {
15         System.out.println("执行实现InitializingBean的初始化方法");
16     }
17 
18     public UserService getUserService() {
19         return userService;
20     }
21 
22     public void setUserService(UserService userService) {
23         System.out.println("属性注入时,执行该方法");
24         this.userService = userService;
25     }
26 }
 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" xmlns:aop="http://www.springframework.org/schema/aop"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
 5 
 6     <bean id="myBean" class="com.lucky.test.spring.demo.MyBean" init-method="initMethod">
 7         <property name="userService" ref="userService"/>
 8     </bean>
 9 
10     <bean id="userService" class="com.lucky.test.spring.demo.impl.UserServiceImpl"/>
11 
12 </beans>

自定义MyBean实现了InitializingBean,并且配置了init-method和注入了一个属性UserService,则此bean加载的时候执行顺序应该是

1、执行构造器方法MyBean() -> 2.执行setUserSerivce方法属性注入 -> 3.执行afterPropertiesSet()方法 ->4.执行配置的初始化方法initMethod()

测试结果如下:

执行构造器方法
属性注入时,执行该方法
执行实现InitializingBean的初始化方法
执行配置的初始化方法

同理,实现了DisposableBean接口的方法需要实现destroy方法,同样执行顺序也是在配置的销毁方法destroy-method前面执行,所以当bean销毁时的执行顺序为

1、执行DisposableBean接口的destroy()方法

2、执行配置的destroy-method的方法

使用案例如下:

 1 public class MyBean implements InitializingBean, DisposableBean{
 2     
 3     @Override
 4     public void destroy() throws Exception {
 5         System.out.println("执行实现了DisposableBean接口的destroy方法");
 6     }
 7 
 8     public void destroyMethod(){
 9         System.out.println("执行配置的销毁方法destroyMethod");
10     }
11 
12     private UserService userService;
13 
14     public MyBean(){
15         System.out.println("执行构造器方法");
16     }
17 
18     public void initMethod(){
19         System.out.println("执行配置的初始化方法");
20     }
21 
22     @Override
23     public void afterPropertiesSet() throws Exception {
24         System.out.println("执行实现InitializingBean的初始化方法");
25     }
26 
27     public UserService getUserService() {
28         return userService;
29     }
30 
31     public void setUserService(UserService userService) {
32         System.out.println("属性注入时,执行该方法");
33         this.userService = userService;
34     }
35 }
1 <bean id="myBean" class="com.lucky.test.spring.demo.MyBean" init-method="initMethod" destroy-method="destroyMethod">
2         <property name="userService" ref="userService"/>
3     </bean>

将MyBean实现了DisposableBean接口,实现了destroy方法,则销毁该bean的时候会先执行destroy方法,再执行配置的destroyMethod方法,测试结果如下:

1 执行实现了DisposableBean接口的destroy方法
2 执行配置的销毁方法destroyMethod

二、InitializingBean和DisposableBean接口的实现原理

目前我们可以了解到实现了InitializingBean和DisposableBean接口的bean,会在bean初始化和销毁的时候分别执行这两个方法,那么具体是如何执行的呢?不妨从源码来分析看看。

首先看初始化方法是如何执行的,从Spring容器中获取bean需要执行调用BeanFactory的getBean方法,如果不存在就进行初始化,在初始化的过程中,核心步骤是执行了doCreateBean方法

该方法核心流程主要有三步,分别如下:

 1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
 2             throws BeanCreationException {
 3         //...
 4         /** 1.通过构造器创建bean*/
 5         createBeanInstance(beanName, mbd, args);
 6         /** 2.bean属性填充(依赖注入其他的bean)*/
 7         populateBean(beanName, mbd, instanceWrapper);
 8         /** 3.执行bean的初始化方法*/
 9         exposedObject = initializeBean(beanName, exposedObject, mbd);
10         //...
11         return exposedObject;
12     }

其中执行bean初始化的方法就是initializeBean方法,源码如下:

 1 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
 2         if (System.getSecurityManager() != null) {
 3             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 4                 invokeAwareMethods(beanName, bean);
 5                 return null;
 6             }, getAccessControlContext());
 7         }
 8         else {
 9             invokeAwareMethods(beanName, bean);
10         }
11 
12         Object wrappedBean = bean;
13         if (mbd == null || !mbd.isSynthetic()) {
14             wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
15         }
16 
17         try {
18                        /** 执行初始化方法具体逻辑*/
19             invokeInitMethods(beanName, wrappedBean, mbd);
20         }
21         catch (Throwable ex) {
22             throw new BeanCreationException(
23                     (mbd != null ? mbd.getResourceDescription() : null),
24                     beanName, "Invocation of init method failed", ex);
25         }
26         if (mbd == null || !mbd.isSynthetic()) {
27             wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
28         }
29 
30         return wrappedBean;
31     }

此处最终又调用了 invokeInitMethods方法来执行初始化方法,源码如下:

 1 protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
 2             throws Throwable {
 3 
 4         /** 1.判断当前bean是否是 InitializingBean的实现类  */
 5         boolean isInitializingBean = (bean instanceof InitializingBean);
 6         if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
 7 
 8             if (System.getSecurityManager() != null) {
 9                 try {
10                     /** 2.将bean转化成InitializingBean类型,直接调用afterPropertiesSet方法*/
11                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
12                         ((InitializingBean) bean).afterPropertiesSet();
13                         return null;
14                     }, getAccessControlContext());
15                 }
16                 catch (PrivilegedActionException pae) {
17                     throw pae.getException();
18                 }
19             }
20             else {
21                 /** 2.将bean转化成InitializingBean类型,直接调用afterPropertiesSet方法*/
22                 ((InitializingBean) bean).afterPropertiesSet();
23             }
24         }
25 
26         if (mbd != null && bean.getClass() != NullBean.class) {
27             /** 3.获取bean定义的init-method方法*/
28             String initMethodName = mbd.getInitMethodName();
29             /** 4.判断init-method方法是否存在,且方法名不是afterPropertiesSet方法(因为afterPropertiesSet方法已经执行过了)*/
30             if (StringUtils.hasLength(initMethodName) &&
31                     !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
32                     !mbd.isExternallyManagedInitMethod(initMethodName)) {
33                 /** 5.通过反射调用init-method*/
34                 invokeCustomInitMethod(beanName, bean, mbd);
35             }
36         }
37     }

从源码可以看出,在invokeInitMethods方法中,会先判断当前bean是否实现了InitializingBean接口,如果实现了该接口,则直接将该bean转化为InitializingBean类型,然后直接执行afterPropertiiesSet方法;然后再判断bean是否配置了init-method,

如果配置了init-method且方法名有效,则通过反射来执行init-method方法。另外这两个初始化方法是在同一个函数中执行的,所以说如果afterPropertiesSet方法执行报错了,那么init-method方法就不会再执行了。

再看下bean的销毁过程,当Spring容器销毁时,会将容器中的所有单例bean先全部销毁,在ApplicationContext中的destroyBeans()方法就是用来处理销毁bean的任务的,源码如下:

1 protected void destroyBeans() {
2               /** 调用destroySingletons()方法*/
3         getBeanFactory().destroySingletons();
4 } 1 public void destroySingleton(String beanName) {
 2         //将bean从三级缓存中删除
 3         removeSingleton(beanName);
 4 
 5         //从disposableBeans中获取该bean的DisposableBean对象
 6         DisposableBean disposableBean;
 7         synchronized (this.disposableBeans) {
 8             disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
 9         }
//执行destroyBean方法
10 destroyBean(beanName, disposableBean); 11 }
 1 protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
 2         // Trigger destruction of dependent beans first...
 3         Set<String> dependencies;
 4         synchronized (this.dependentBeanMap) {
 5             // Within full synchronization in order to guarantee a disconnected Set
 6             dependencies = this.dependentBeanMap.remove(beanName);
 7         }
 8         if (dependencies != null) {
 9             if (logger.isTraceEnabled()) {
10                 logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
11             }
12             for (String dependentBeanName : dependencies) {
13                 destroySingleton(dependentBeanName);
14             }
15         }
16 
17         // 执行DisposableBean的destroy方法,因为前面将每个bean都转化成了DisposableBean对象,所以每个对象都会执行destroy方法
18         if (bean != null) {
19             try {
20                 bean.destroy();
21             }
22             catch (Throwable ex) {
23                 if (logger.isWarnEnabled()) {
24                     logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
25                 }
26             }
27         }
28 
29         // Trigger destruction of contained beans...
30         Set<String> containedBeans;
31         synchronized (this.containedBeanMap) {
32             // Within full synchronization in order to guarantee a disconnected Set
33             containedBeans = this.containedBeanMap.remove(beanName);
34         }
35         if (containedBeans != null) {
36             for (String containedBeanName : containedBeans) {
37                 destroySingleton(containedBeanName);
38             }
39         }
40 
41         // Remove destroyed bean from other beans' dependencies.
42         synchronized (this.dependentBeanMap) {
43             for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
44                 Map.Entry<String, Set<String>> entry = it.next();
45                 Set<String> dependenciesToClean = entry.getValue();
46                 dependenciesToClean.remove(beanName);
47                 if (dependenciesToClean.isEmpty()) {
48                     it.remove();
49                 }
50             }
51         }
52 
53         // Remove destroyed bean's prepared dependency information.
54         this.dependenciesForBeanMap.remove(beanName);
55     }

调用bean的封装类DisposableBeanAdapter的destroy方法,源码如下:

 1 public void destroy() {
 2         if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
 3             for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
 4                 processor.postProcessBeforeDestruction(this.bean, this.beanName);
 5             }
 6         }
 7 
 8         if (this.invokeDisposableBean) {
 9             if (logger.isTraceEnabled()) {
10                 logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
11             }
12             try {
13                 if (System.getSecurityManager() != null) {
14                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
15                         ((DisposableBean) this.bean).destroy();
16                         return null;
17                     }, this.acc);
18                 }
19                 else {
20                     ((DisposableBean) this.bean).destroy();
21                 }
22             }
23             catch (Throwable ex) {
24                 String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
25                 if (logger.isDebugEnabled()) {
26                     logger.warn(msg, ex);
27                 }
28                 else {
29                     logger.warn(msg + ": " + ex);
30                 }
31             }
32         }
33 
34         if (this.destroyMethod != null) {
35             invokeCustomDestroyMethod(this.destroyMethod);
36         }
37         else if (this.destroyMethodName != null) {
38             Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
39             if (methodToInvoke != null) {
40                 invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
41             }
42         }
43     }

可以看出销毁的逻辑和初始化的逻辑基本是一致,先从DisposableBeanAdapter对象中获取当前bean对象转化成DisposableBean对象,然后直接调用destroy()方法;然后再通过反射调用bean配置的destroyMethod方法。

在容器中有个集合会保存所有需要销毁的bean的集合,也就是上面第二块代码中的Map<String, Object>disposableBeans,当每个bean初始化之后,也就是doCreateBean方法中,当执行完全部初始化方法之后,会执行一个registerDisposableBeanIfNecessary方法,该方法的作用是将当前bean封装成一个销毁对象DisposabelBean的实现类DisposableBeanAdapter,然后加入到disposableBeans中,所以当容器需要销毁时,会遍历这个集合依次调用DisposableBeanAdapter对象的destroy方法。

总结:

1、InitializingBean和DisposableBean接口的方法分别在bean初始化和bean销毁的时候执行

2、InitializingBean和DisposableBean接口的方法执行顺序是在bean自定义的初始化方法init-method和destroy-method之前执行

3、InitializingBean和DisposableBean接口的方法是将bean强制转换成该接口类型,直接调用对应的方法;而init-method和destroy-method方法的调用是通过反射来执行

4、通过接口的方式效率更高,但是和Spring框架API耦合,需要实现Spring提供的接口;自定义方法方式使用反射效率低,但是不需要依赖Spring的API

5、两种初始化方法是在同一个线程中同一个方法中执行的,所以先执行的接口方法如果报错,那么自定义配置的方法则不会再执行

原文地址:https://www.cnblogs.com/jackion5/p/13274098.html