疑問:Spring 中構造器、init-method、@PostConstruct、afterPropertiesSet 孰先孰后,自動注入發生時間


一、前言

spring的一大優點就是擴展性很強,比如,在spring bean 的生命周期中,給我們預留了很多參與bean 的生命周期的方法。
大致梳理一下,有以下幾種:
  • 通過實現 InitializingBean/DisposableBean 接口來定制初始化之后/銷毀之前的操作方法;
  • 通過 <bean> 元素的 init-method/destroy-method屬性指定初始化之后 /銷毀之前調用的操作方法;
  • 在指定方法上加上@PostConstruct 或@PreDestroy注解來制定該方法是在初始化之后還是銷毀之前調用;
  • 自定義 org.springframework.beans.factory.config.BeanPostProcessor ,來讓 spring 回調我們的方法來參與 bean的生命周期。

 

但有個問題是,如果同時使用上面四種方式,會是什么結果? 誰先,誰后呢?

 

二、驗證

1、新建工程

我這邊建立了測試工程,源碼在文末:

 

xml中定義如下:

<bean class="com.ckl.springbeanlifecycle.DemoController" init-method="init" destroy-method="cleanUp"></bean>

 

重點是我們的DemoController,該類作為一個 bean,在xml中已經定義了,我們為該類,實現了各種接口,以及各種生命周期的相關注解:

 1 package com.ckl.springbeanlifecycle;  2 
 3 import com.ckl.springbeanlifecycle.service.IDemoService;  4 import org.springframework.beans.factory.DisposableBean;  5 import org.springframework.beans.factory.InitializingBean;  6 import org.springframework.beans.factory.annotation.Autowired;  7 
 8 import javax.annotation.PostConstruct;  9 import javax.annotation.PreDestroy; 10 
11 /**
12  * desc: 13  * 14  * @author : caokunliang 15  * creat_date: 2019/7/20 0020 16  * creat_time: 18:45 17  **/
19 public class DemoController implements InitializingBean,DisposableBean { 20 
21  @Autowired 22     private IDemoService iDemoService; 23     
24     public DemoController() { 25  System.out.println(); 26         System.out.println("constructor "); 27         System.out.println( "屬性:" + iDemoService); 28  System.out.println(); 29  } 30 
31  @Override 32     public void destroy() throws Exception { 33  System.out.println(); 34         System.out.println("implements DisposableBean interface"); 35         System.out.println( "屬性iDemoService已注入:" + (iDemoService != null)); 36         System.out.println( "屬性iDemoService已注入:" + iDemoService); 37  System.out.println(); 38  } 39 
40  @Override 41     public void afterPropertiesSet() throws Exception { 42  System.out.println(); 43         System.out.println("afterPropertiesSet interface"); 44         System.out.println( "屬性iDemoService已注入:" + (iDemoService != null)); 45         System.out.println( "屬性iDemoService已注入:" + iDemoService); 46  System.out.println(); 47  } 48 
49  @PostConstruct 50     public void postConstruct(){ 51  System.out.println(); 52         System.out.println("@PostConstrut...."); 53         System.out.println( "屬性iDemoService已注入:" + (iDemoService != null)); 54         System.out.println( "屬性iDemoService已注入:" + iDemoService); 55  System.out.println(); 56  } 57 
58  @PreDestroy 59     public void preDestroy(){ 60  System.out.println(); 61         System.out.println("@PreDestroy....."); 62         System.out.println( "屬性iDemoService已注入:" + iDemoService); 63  System.out.println(); 64  } 65 
66     public void init(){ 67  System.out.println(); 68         System.out.println("init-method by xml 配置文件"); 69         System.out.println( "屬性iDemoService已注入:" + (iDemoService != null)); 70  System.out.println(); 71  }
72 public void cleanUp(){ 73 System.out.println(); 74 System.out.println("destroy-method by xml 配置文件"); 75 System.out.println( "屬性iDemoService已注入:" + iDemoService); 76 System.out.println(); 77 } 78 }

 

因為我們還需要驗證 org.springframework.beans.factory.config.BeanPostProcessor,所以我們自定義了一個 org.springframework.beans.factory.config.BeanPostProcessor:

 1 package com.ckl.springbeanlifecycle;  2 
 3 import org.springframework.beans.BeansException;  4 import org.springframework.beans.factory.config.BeanPostProcessor;  5 import org.springframework.stereotype.Component;  6 
 7 import java.lang.reflect.Field;  8 
 9 /**
10  * desc: 11  * 12  * @author : caokunliang 13  * creat_date: 2019/7/20 0020 14  * creat_time: 18:52 15  **/
16 @Component 17 public class MyBeanPostProcessor implements BeanPostProcessor {
18 @Override 19 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 20 if (bean instanceof DemoController){ 21 System.out.println(); 22 System.out.println("BeanPostProcessor:" + "postProcessBeforeInitialization"); 23 Field field = null; 24 try { 25 field = bean.getClass().getDeclaredField("iDemoService"); 26 field.setAccessible(true); 27 } catch (NoSuchFieldException e) { 28 e.printStackTrace(); 29 } 30 try { 31 Object o = field.get(bean); 32 System.out.println( "屬性iDemoService已注入:" + (o != null)); 33 System.out.println( "屬性iDemoService已注入:" + o); 34 System.out.println(); 35 } catch (IllegalAccessException e) { 36 e.printStackTrace(); 37 } 38 } 39 return bean; 40 } 41 42 @Override 43 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 44 if (bean instanceof DemoController){ 45 System.out.println(); 46 System.out.println("BeanPostProcessor:" + "postProcessAfterInitialization"); 47 Field field = null; 48 try { 49 field = bean.getClass().getDeclaredField("iDemoService"); 50 field.setAccessible(true); 51 } catch (NoSuchFieldException e) { 52 e.printStackTrace(); 53 } 54 try { 55 Object o = field.get(bean); 56 System.out.println( "屬性iDemoService已注入:" + (o != null)); 57 System.out.println( "屬性iDemoService已注入:" + o); 58 System.out.println(); 59 } catch (IllegalAccessException e) { 60 e.printStackTrace(); 61 } 62 } 63 return bean; 64 } 65 }

 

2、運行

初始化時的順序如下:

 

關閉容器時,執行順序為:

 

 

3、擴展

可能部分朋友,還有這樣的需求,即,在程序啟動完成后,做點事情,比如,預熱緩存之類的,那么,你可以這樣:

 2 
 3 import org.springframework.context.ApplicationContext;  4 import org.springframework.context.ApplicationListener;  5 import org.springframework.context.event.ContextRefreshedEvent;  6 import org.springframework.stereotype.Service;  7 
 8 /**
 9  * desc: 10  * 11  * @author : caokunliang 12  * creat_date: 2018/11/20 0020 13  * creat_time: 14:50 14  **/
15 @Service 16 public class InitRunner implements ApplicationListener<ContextRefreshedEvent> { 17 
18  @Override 19     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { 20         //root application context
21         if (contextRefreshedEvent.getApplicationContext().getParent() == null) { 22             /**
23  * 這里既是 root 容器初始化完畢后,會進入該分支, 24              */
25              todo。。。
26 
27         }else { 28             /** 
29  * 如果是spring mvc的話, dispatchServlet對應的 applicationContext 會進入這個分支 30              */
31             
32             ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext(); 33             todo。。。
34  } 35 
36 
37 
38  } 39 }

 

上面是針對啟動時做的事情,關閉時,如果想做點事情,可以這樣:

 1 package com.ceiec.webservice.init;  2 
 3 import org.slf4j.Logger;  4 import org.slf4j.LoggerFactory;  5 import org.springframework.context.ApplicationListener;  6 import org.springframework.context.event.ContextClosedEvent;  7 import org.springframework.stereotype.Service;  8 
 9 /**
10  * desc: 11  * 12  * @author : caokunliang 13  * creat_date: 2018/11/20 0020 14  * creat_time: 14:50 15  **/
16 @Service 17 public class CloseRunner implements ApplicationListener<ContextClosedEvent> { 18     private static final Logger logger = LoggerFactory.getLogger(CloseRunner.class); 19 
20  @Override 21     public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { 22         logger.info("clean resources when close applicationContext"); 23         if(contextClosedEvent.getApplicationContext().getParent() == null){ 24 
25  } 26 
27  } 28 }

 

4、spring boot 的擴展

針對spring boot,也可以注冊我們的 listener來參與生命周期。

實現 org.springframework.boot.SpringApplicationRunListener 即可,並將自定義的 listener 配置到 meta-inf 下的 spring.factories 文件。

舉例如下:

 1 package com.ceiec.router.config;  2 
 3 import com.ceiec.router.config.servletconfig.MyServletContext;  4 import lombok.Data;  5 import lombok.extern.slf4j.Slf4j;  6 import org.springframework.boot.SpringApplication;  7 import org.springframework.boot.SpringApplicationRunListener;  8 import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;  9 import org.springframework.context.ConfigurableApplicationContext; 10 import org.springframework.core.env.ConfigurableEnvironment; 11 import org.springframework.core.env.MapPropertySource; 12 import org.springframework.core.env.MutablePropertySources; 13 import org.springframework.core.env.PropertySource; 14 
15 import javax.servlet.ServletContext; 16 import java.util.Map; 17 
18 /**
19  * desc: 20  * 21  * @author : caokunliang 22  * creat_date: 2019/5/24 0024 23  * creat_time: 20:07 24  **/
25 @Data 26 @Slf4j 27 public class MyListener implements SpringApplicationRunListener { 28 
29 
30     public MyListener(SpringApplication application, String[] args) { 31         super(); 32  } 33 
34 
35  @Override 36     public void starting() { 37 
38  } 39 
40  @Override 41     public void environmentPrepared(ConfigurableEnvironment environment) { 42         MutablePropertySources propertySources = environment.getPropertySources(); 43         for (PropertySource<?> propertySource : propertySources) { 44             Object value = propertySource.getProperty("spring.liveBeansView.mbeanDomain"); 45 
46             if (value != null) { 47                 MapPropertySource source = (MapPropertySource) propertySource; 48                 Map<String, Object> map = source.getSource(); 49                 map.remove("spring.liveBeansView.mbeanDomain"); 50 
51                 log.info("spring.liveBeansView.mbeanDomain: after: {}",propertySource.getProperty("spring.liveBeansView.mbeanDomain")); 52  } 53  } 54  } 55 
56  @Override 57     public void contextPrepared(ConfigurableApplicationContext context) { 58         log.info("contextPrepared"); 59         ServletContext servletContext = new MyServletContext(); 60         ServletWebServerApplicationContext applicationContext = (ServletWebServerApplicationContext) context; 61  applicationContext.setServletContext(servletContext); 62  } 63 
64  @Override 65     public void contextLoaded(ConfigurableApplicationContext context) { 66         //Not used.
67  } 68 
69  @Override 70     public void started(ConfigurableApplicationContext context) { 71 
72  } 73 
74  @Override 75     public void running(ConfigurableApplicationContext context) { 76 
77  } 78 
79  @Override 80     public void failed(ConfigurableApplicationContext context, Throwable exception) { 81 
82  } 83 
84 
85 }

 

 

 

 源碼在交友網站: https://github.com/cctvckl/spring-bean-lifecycle


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM