Spring Bean的初始化和销毁方式详解

转自:https://blog.csdn.net/liuchuanhong1/article/details/52939353

参考:https://blog.csdn.net/heyutao007/article/details/50326793

最近在项目中需要封装kafka的服务,其中使用到了工厂模式,该模式涉及到了Spring Bean的初始化和销毁,如是研究了一番,总结如下,和大家共勉之

Spring Bean的初始化和销毁Bean有几种方法了?答案是3

方法一:使用@PostConstruct注解初始化,使用@PreDestroy注解销毁Bean

示例代码如下:

[java]  view plain  copy
  1. public class PostConstructInit {  
  2.     @PostConstruct  
  3.     public void PostConstruct() {  
  4.         System.out.println("执行PostConstructInit: PostConstruct");  
  5.     }  
  6.       
  7.     @PreDestroy  
  8.     public void PreDestory(){  
  9.         System.out.println("执行PostConstructInit: PreDestory");  
  10.     }  
  11.       
  12.     public static void main(String[] args) {  
  13.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  
  14.                 "classpath:beans.xml");  
  15.         context.close();  
  16.     }  
  17. }  

配置文件如下:

[html]  view plain  copy
  1. <context:annotation-config/>  
  2. <context:component-scan base-package="com.chhliu.myself.spring.test"></context:component-scan>  
  3. <bean id="postConstructor" class="com.chhliu.myself.spring.test.PostConstructInit"></bean>  

测试结果如下:

执行PostConstructInit: PostConstruct

执行PostConstructInit: PreDestory

方法二:实现InitializingBean, DisposableBean这两个接口,并复写afterPropertiesSet()destroy()方法

示例代码如下:

[java]  view plain  copy
  1. public class InitializingDisposableInit implements InitializingBean,  
  2.         DisposableBean {  
  3.   
  4.     @Override  
  5.     public void destroy() throws Exception {  
  6.         System.out.println("执行InitializingDisposableInit: destroy");  
  7.     }  
  8.   
  9.     @Override  
  10.     public void afterPropertiesSet() throws Exception {  
  11.         System.out.println("执行InitializingDisposableInit: afterPropertiesSet");  
  12.     }  
  13.       
  14.     public static void main(String[] args) {  
  15.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  
  16.                 "classpath:beans-impl.xml");  
  17.         context.close();  
  18.     }  
  19. }  
测试结果如下:

执行InitializingDisposableInit: afterPropertiesSet

执行InitializingDisposableInit: destroy

方法三:使用init-methoddestroy-method配置方法

示例代码如下:

[java]  view plain  copy
  1. public class MethodInit {  
  2.       
  3.     public void initMethod() {  
  4.         System.out.println("执行MethodInit: init-method");  
  5.     }  
  6.   
  7.     public void destroyMethod() {  
  8.         System.out.println("执行MethodInit: destroy-method");  
  9.     }  
  10.       
  11.     public static void main(String[] args) {  
  12.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  
  13.                 "classpath:beans-method.xml");  
  14.         context.close();  
  15.     }  
  16. }  

配置文件如下:

[html]  view plain  copy
  1. <bean id="methodInit" class="com.chhliu.myself.spring.test.MethodInit" init-method="initMethod" destroy-method="destroyMethod"></bean>  
测试结果如下:

执行MethodInit: init-method

执行MethodInit: destroy-method

那么三种初始化和销毁Spring Bean的方式就讲完了,那么这三种方式的执行顺序是如何了,我们接着来做个测试

测试代码如下:

[java]  view plain  copy
  1. public class InitAndDestroySeqBean implements InitializingBean, DisposableBean {  
  2.     public InitAndDestroySeqBean() {  
  3.         System.out.println("执行InitAndDestroySeqBean: 构造方法");  
  4.     }  
  5.   
  6.     @PostConstruct  
  7.     public void postConstruct() {  
  8.         System.out.println("执行InitAndDestroySeqBean: postConstruct");  
  9.     }  
  10.   
  11.     @Override  
  12.     public void afterPropertiesSet() throws Exception {  
  13.         System.out.println("执行InitAndDestroySeqBean: afterPropertiesSet");  
  14.     }  
  15.   
  16.     public void initMethod() {  
  17.         System.out.println("执行InitAndDestroySeqBean: init-method");  
  18.     }  
  19.   
  20.     @PreDestroy  
  21.     public void preDestroy() {  
  22.         System.out.println("执行InitAndDestroySeqBean: preDestroy");  
  23.     }  
  24.   
  25.     @Override  
  26.     public void destroy() throws Exception {  
  27.         System.out.println("执行InitAndDestroySeqBean: destroy");  
  28.     }  
  29.   
  30.     public void destroyMethod() {  
  31.         System.out.println("执行InitAndDestroySeqBean: destroy-method");  
  32.     }  
  33.   
  34.     public static void main(String[] args) {  
  35.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(  
  36.                 "classpath:applicationContext.xml");  
  37.         context.close();  
  38.     }  
  39. }  

测试结果如下:

[java]  view plain  copy
  1. 执行InitAndDestroySeqBean: 构造方法  
  2. 执行InitAndDestroySeqBean: postConstruct  
  3. 执行InitAndDestroySeqBean: afterPropertiesSet  
  4. 执行InitAndDestroySeqBean: init-method  
  5. 执行InitAndDestroySeqBean: preDestroy  
  6. 执行InitAndDestroySeqBean: destroy  
  7. 执行InitAndDestroySeqBean: destroy-method  

最后我们得出的结果如下:

[java]  view plain  copy
  1. 初始化顺序:  
  2. Constructor > @PostConstruct >InitializingBean > init-method  
  3. 销毁的顺序如下:  
  4. @PreDestroy > DisposableBean > destroy-method  
最后,我们总结下这几种方式的特点

1、如果我们在afterPropertiesSet()方法中抛出异常,那么初始化会被终止,不会执行后面的init-method对应的初始化方法

2、Spring中初始化的Bean的类为

Org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

3、afterPropertiesSet()方法的效率比init-method高,因为init-method使用的是反射来寻找对应的方法,而afterPropertiesSet()则是直接执行的,相关源码如下:

[java]  view plain  copy
  1. init-method:  
  2. String initMethodName = mbd.getInitMethodName();  
  3.         final Method initMethod = (mbd.isNonPublicAccessAllowed() ?  
  4.                 BeanUtils.findMethod(bean.getClass(), initMethodName) :  
  5.                 ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));  
  6. 从上面的代码可以看出,使用的是放射方式  
  7.   
  8. afterPropertiesSet():  
  9. protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)  
  10.             throws Throwable {  
  11. // 如果存在afterPropertiesSet方法且实例是InitializingBean类型的话,就直接执行afterPropertiesSet()方法  
  12.         boolean isInitializingBean = (bean instanceof InitializingBean);  
  13.         if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {  
  14.             if (logger.isDebugEnabled()) {  
  15.                 logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");  
  16.             }  
  17.             if (System.getSecurityManager() != null) {  
  18.                 try {  
  19.                     AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
  20.                         public Object run() throws Exception {  
  21.                             ((InitializingBean) bean).afterPropertiesSet();  
  22.                             return null;  
  23.                         }  
  24.                     }, getAccessControlContext());  
  25.                 }  
  26.                 catch (PrivilegedActionException pae) {  
  27.                     throw pae.getException();  
  28.                 }  
  29.             }  
  30.             else {  
  31.                 ((InitializingBean) bean).afterPropertiesSet();  
  32.             }  
  33.         }  
  34. }  

4、init-method只能是无参无返回的public


通过上述输出结果,说明三种初始化的顺序是:
Constructor > @PostConstruct > InitializingBean > init-method

原因:
@PostConstruct注解后的方法在BeanPostProcessor前置处理器中就被执行了。我们知道BeanPostProcessor接口是一个回调的作用,Spring容器的每个受管Bean在调用初始化方法之前,都会获得BeanPostProcessor接口实现类的一个回调。在BeanPostProcessor的方法中有一段逻辑就是会判断当前被回调的bean的方法中有没有被initAnnotationType/destroyAnnotationType注释,如果有,则添加到init/destroy队列中,后续一一执行。initAnnotationType/destroyAnnotationType注解就是@PostConstruct/@PreDestroy。所以@PostConstruct当然要先于InitializingBean和init-method执行了。



从图中,我们可以看到实例化Bean的过程中有以下几个节点:
1)设置属性值;
2)调用Bean中的BeanNameAware.setBeanName()方法,如果该Bean实现了BeanNameAware接口;
3)调用Bean中的BeanFactoryAware.setBeanFactory()方法,如果该Bean实现了BeanFactoryAware接口;
4)调用BeanPostProcessors.postProcessBeforeInitialization()方法;@PostConstruct注解后的方法就是在这里被执行的
5)调用Bean中的afterPropertiesSet方法,如果该Bean实现了InitializingBean接口;
6)调用Bean中的init-method,通常是在配置bean的时候指定了init-method,例如:<bean class="beanClass" init-method="init"></bean>
7)调用BeanPostProcessors.postProcessAfterInitialization()方法;
8)如果该Bean是单例的,则当容器销毁并且该Bean实现了DisposableBean接口的时候,调用destory方法;如果该Bean是prototype,则将准备好的Bean提交给调用者,后续不再管理该Bean的生命周期。


猜你喜欢

转载自blog.csdn.net/liwenxia626/article/details/80771523