• 整理在Spring IOC容器初始化后可以处理特定逻辑的多种实现方式


      Spring框架的核心是依赖注入、切面;Spring Boot是在Spring框架的基础上为其提供许多默认配置、默认约定(约定优于配置),从而达到减少或减化配置进而可开箱即用、快速上手;Spring Cloud又是在Spring Boot框架的基础上提供了大量的微服务体系内的各种组件(starter),简化了微服务开发实现的成本;但不管是Spring、Spring Boot、Spring Cloud的底层实现都是充分利用了IOC、AOP;有时我们想在所有Bean都成功注册到IOC容器后,并实例化完成后统一做一些初始化的工作,那么就需要捕获Spring IOC容器初始化后的事件点,而这个事件点我结合自己经验及网上分享进行了一次整理汇总,主要是有如下几种方式(先后触发顺序):

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

    下面是把如上的触发点都集中实现在一个Bean中看效果,示例代码如下:

    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项目中,就可以看到运行的效果(执行的顺序) ,比如我这里的输出如下:


    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.$Proxy84

    从输出结果我们可以看出整个的顺序,那么也得出结论最好是在:SmartLifecycle.start、ApplicationListener.onApplicationEvent、ApplicationRunner.run 这三种实现方式中才能真正达到IOC初始后全部完成后触发的事件点的要求。

    注:如果需要在控制台直接看到上面的内容,建议调整spring root的log level,避免无效的日志输出影响观看,配置如:

    logging.level.root=error
  • 相关阅读:
    IBM X System ServerGuide 8.41 服务器 系统安装 引导盘
    oracle查找重复记录
    linux的nohup命令的用法。
    最佳设计工作流
    开发的浏览器颠覆微软,引爆了一场互联网大战
    程序员的成长和代码行数的关系
    Oracle SQL Developer 连接 MySQL
    Linux 如何打开端口
    SQLSERVER中如何快速比较两张表的不一样
    我所理解的MVVM
  • 原文地址:https://www.cnblogs.com/zuowj/p/11871415.html
Copyright © 2020-2023  润新知