一、前言
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 }