zoukankan      html  css  js  c++  java
  • 疑问: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

  • 相关阅读:
    algorithm@ O(3/2 n) time to findmaximum and minimum in a array
    algorithm@ lower_bound implementation(Binary Search)
    leetcode@ [241] Different Ways to Add Parentheses (Divide and Conquer)
    java@ 利用ArrayList实现dijkstra算法以及topological 排序算法(java.util.ArrayList)
    leetcode@ [329] Longest Increasing Path in a Matrix (DFS + 记忆化搜索)
    Dubbo服务重载方法在JDK1.8上调用出错的问题(待解决)
    数据结构之线性表-链式存储之循环链表(三)
    数据结构之线性表-链式存储之静态链表(二)
    网络知识小笔记
    书中自有颜如玉,书中自有黄金屋(尼采篇)
  • 原文地址:https://www.cnblogs.com/grey-wolf/p/6627925.html
Copyright © 2011-2022 走看看