整理在Spring IOC容器初始化後能夠處理特定邏輯的多種實現方式

  Spring框架的核心是依賴注入、切面;Spring Boot是在Spring框架的基礎上爲其提供許多默認配置、默認約定(約定優於配置),從而達到減小或減化配置進而可開箱即用、快速上手;Spring Cloud又是在Spring Boot框架的基礎上提供了大量的微服務體系內的各類組件(starter),簡化了微服務開發實現的成本;但無論是Spring、Spring Boot、Spring Cloud的底層實現都是充分利用了IOC、AOP;有時咱們想在全部Bean都成功註冊到IOC容器後,並實例化完成後統一作一些初始化的工做,那麼就須要捕獲Spring IOC容器初始化後的事件點,而這個事件點我結合本身經驗及網上分享進行了一次整理彙總,主要是有以下幾種方式(前後觸發順序):java

  1. ApplicationContextAware.setApplicationContext
  2. Bean 添加了@PostConstruct的方法
  3. InitializingBean.afterPropertiesSet
  4. BeanPostProcessor (postProcessBeforeInitialization、postProcessAfterInitialization)
  5. SmartLifecycle.start
  6. ApplicationListener.onApplicationEvent
  7. ApplicationRunner.run

 

下面是把如上的觸發點都集中實如今一個Bean中看效果,示例代碼以下:web

package cn.zuowenjun.demo.ioc.service;

import cn.zuowenjun.demo.ioc.mapper.DemoInfoMapper;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

import javax.annotation.PostConstruct;
import java.util.Date;

@Component
public class DemoModesCollection
        implements ApplicationContextAware,
        ApplicationListener<ContextRefreshedEvent>,
        ApplicationRunner,
        SmartLifecycle,
        InitializingBean,
        BeanPostProcessor {

    private  ApplicationContext context;


    @PostConstruct
    public void postConstructMethod(){
        System.out.printf("%1$tF %1$tT.%1$tL --->@PostConstruct postConstructMethod:running!%n",new Date());
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);

        System.out.println("@PostConstruct >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.printf("%1$tF %1$tT.%1$tL --->InitializingBean.afterPropertiesSet:running!%n",new Date());
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("afterPropertiesSet >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.printf("%1$tF %1$tT.%1$tL --->ApplicationContextAware.setApplicationContext:running! ===beansCount:%2$d %n",
                new Date(), applicationContext.getBeanDefinitionCount());
        this.context=applicationContext;
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("setApplicationContext >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        if (event.getApplicationContext().getParent() == null || event.getApplicationContext() instanceof WebApplicationContext) {
            System.out.printf("%1$tF %1$tT.%1$tL --->ApplicationListener.onApplicationEvent:running! ===beansCount:%2$d  (beanType:%3$s) %n",
                    new Date(), event.getApplicationContext().getBeanDefinitionCount(), event.getApplicationContext().getClass().getTypeName());

            DemoInfoMapper mapper= event.getApplicationContext().getBean(DemoInfoMapper.class);
            System.out.println("onApplicationEvent >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.printf("%1$tF %1$tT.%1$tL --->ApplicationRunner.run:running!%n", new Date());
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("run >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
    }



    private boolean isRunning = false;

    @Override
    public void start() {

        System.out.printf("%1$tF %1$tT.%1$tL --->SmartLifecycle.start:running!%n",new Date());

        isRunning=true;

        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("start >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
    }

    @Override
    public void stop() {
        isRunning = false;
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }



    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.printf("%1$tF %1$tT.%1$tL --->BeanPostProcessor.postProcessBeforeInitialization:running!%n",new Date());
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("postProcessBeforeInitialization >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.printf("%1$tF %1$tT.%1$tL --->BeanPostProcessor.postProcessAfterInitialization:running!%n",new Date());
        DemoInfoMapper mapper= context.getBean(DemoInfoMapper.class);
        System.out.println("postProcessAfterInitialization >>>context.getBean:" + (mapper==null?"null":AopUtils.getTargetClass(mapper).getName()));
        return bean;
    }
}

 如上代碼,你們可將嘗試其加入到一個spring boot項目中,就能夠看到運行的效果(執行的順序) ,好比我這裏的輸出以下:spring


2019-11-16 12:25:48.283 --->ApplicationContextAware.setApplicationContext:running! ===beansCount:325
setApplicationContext >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:48.558 --->@PostConstruct postConstructMethod:running!
@PostConstruct >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:48.559 --->InitializingBean.afterPropertiesSet:running!
afterPropertiesSet >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:48.567 --->BeanPostProcessor.postProcessBeforeInitialization:running!
postProcessBeforeInitialization >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:48.567 --->BeanPostProcessor.postProcessAfterInitialization:running!
......有不少的BeanPostProcessor.postProcessBeforeInitialization、BeanPostProcessor.postProcessAfterInitialization(每個bean初始化先後都會觸發,省略)
2019-11-16 12:25:50.045 --->SmartLifecycle.start:running!
start >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:50.053 --->ApplicationListener.onApplicationEvent:running! ===beansCount:325 (beanType:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext)
onApplicationEvent >>>context.getBean:com.sun.proxy.$Proxy84
2019-11-16 12:25:50.080 --->ApplicationRunner.run:running!
run >>>context.getBean:com.sun.proxy.$Proxy84app

從輸出結果咱們能夠看出整個的順序,那麼也得出結論最好是在:SmartLifecycle.start、ApplicationListener.onApplicationEvent、ApplicationRunner.run 這三種實現方式中才能真正達到IOC初始後所有完成後觸發的事件點的要求。框架

注:若是須要在控制檯直接看到上面的內容,建議調整spring root的log level,避免無效的日誌輸出影響觀看,配置如:ide

logging.level.root=error
相關文章
相關標籤/搜索