SpringBoot项目启动时初始化操作的几种方式

前言
通常的我们的项目开发中,经常会遇到那种在服务一启动就需要自动执行一些业务代码的情况。比如将数据库中的配置信息或者数据字典之类的缓存到redis,或者在服务启动的时候将一些配置化的定时任务开起来。关于spring mvc或者springboot如何在项目启动的时候就执行一些代码,方法其实有很多,我这边介绍一下我使用过的三种。


1、@PostConstruct 注解

从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。@PostConstruct会在所在类的构造函数执行之后执行,在init()方法执行之前执行。(@PreDestroy注解的方法会在这个类的destory()方法执行之后执行.)
使用示例:在Spring容器加载之后,启动定时任务读取数据库配置的。在这里我使用@PostConstruct 指定了需要启动的方法。
 

@Component // 注意 这里必须有
public class StartAllJobInit {
    protected Logger logger = LoggerFactory.getLogger(getClass().getName());
    @Autowired
    JobInfoService jobInfoService;
 
    @Autowired
    JobTaskUtil jobTaskUtil;
 
    @PostConstruct // 构造函数之后执行
    public void init(){
        System.out.println("容器启动后执行");
        startJob();
    }
 
    public void startJob() {
        List<JobInfoBO> list = jobInfoService.findList();
        for (JobInfoBO jobinfo :list) {
            try {
                if("0".equals(jobinfo.getStartWithrun())){
                    logger.info("任务{}未设置自动启动。", jobinfo.getJobName());
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
                }else{
                    logger.info("任务{}设置了自动启动。", jobinfo.getJobName());
                    jobTaskUtil.addOrUpdateJob(jobinfo);
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
                }
            } catch (SchedulerException e) {
                logger.error("执行定时任务出错,任务名称 {} ", jobinfo.getJobName());
            }
        }
    }
}

2、实现 @CommandLineRunner 接口并重写run()方法

SpringBoot在项目启动后会遍历所有实现CommandLineRunner的实体类并执行run方法,多个实现类可以并存并且根据order注解排序顺序执行。
同样是启动时执行定时任务,使用这种方式我的写法如下:
 

@Component // 注意 这里必须有
//@Order(2) 如果有多个类需要启动后执行 order注解中的值为启动的顺序
public class StartAllJobInit implements CommandLineRunner {
    protected Logger logger = LoggerFactory.getLogger(getClass().getName());
    @Autowired
    JobInfoService jobInfoService;
 
    @Autowired
    JobTaskUtil jobTaskUtil;
 
    @Override
    public void run(String... args) {
        List<JobInfoBO> list = jobInfoService.findList();
        for (JobInfoBO jobinfo :list) {
            try {
                if("0".equals(jobinfo.getStartWithrun())){
                    logger.info("任务{}未设置自动启动。", jobinfo.getJobName());
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
                }else{
                    logger.info("任务{}设置了自动启动。", jobinfo.getJobName());
                    jobTaskUtil.addOrUpdateJob(jobinfo);
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
                }
            } catch (SchedulerException e) {
                logger.error("执行定时任务出错,任务名称 {} ", jobinfo.getJobName());
            }
        }
    }
}

3、 实现 @ApplicationRunner 接口并重写run()方法

run方法参数是ApplicationArguments,解析封装过后的args参数, 通过该对象既可以拿到原始命令行参数,也可以拿到解析后的参数, 其中 @Order中的值指定了执行顺序,值小的先执行。默认值是Integer.MAX_VALUE
 

import java.util.Arrays;
import java.util.Set;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(1)
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("====================MyApplicationRunner================");
        System.out.println("order值: 1");
        System.out.println("原始参数:"+Arrays.asList(args.getSourceArgs()));
        Set<String> keys = args.getOptionNames();
        for (String key : keys) {
            System.out.println("解析后的key: ["+key+"]  value: "+args.getOptionValues(key));
        }
        System.out.println("无OptionName的参数: "+args.getNonOptionArgs());
        System.out.println("=======================================================");
    }

4、实现org.springframework.beans.factory.InitializingBean接口并重写 afterPropertiesSet()方法 

InitializingBean接口只包含一个方法afterPropertiesSet(),凡是继承了InitializingBean接口的类,在初始化时都会调用这方法;
使用方法分为三个步骤:1、 被spring管理        2、 实现InitializingBean接口         3、重写afterPropertiesSet方法

@Component
public class InitializationBeanTest implements InitializingBean{
    private static final Logger LOG = LoggerFactory.getLogger(InitializationBeanTest.class);
 
    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean 开始了。。。");
        //初始化操作
    }
}

5、使用ContextRefreshedEvent事件(上下文件刷新事件)

ContextRefreshedEvent是Spring的ApplicationContextEvent一个实现,此事件会在Spring容器初始化完成后以及刷新时触发。
在这里我需要在springboot程序启动之后加载配置信息和字典信息到Redis缓存中去,我可以这样写:

@Component // 注意 这个也是必须有的注解 三种都需要 使spring扫描到这个类并交给它管理
public class InitRedisCache implements ApplicationListener<ContextRefreshedEvent> {
    static final Logger logger = LoggerFactory.getLogger(InitRedisCache.class);
 
    @Autowired
    private SysConfigService sysConfigService;
 
    @Autowired
    private SysDictService sysDictService;
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        logger.info("-------加载配置信息 start-------");
        sysConfigService.loadConfigIntoRedis();
        logger.info("-------加载配置信息 end-------");
 
        logger.info("-------加载字典信息 start-------");
        sysDictService.loadDictIntoRedis();
        logger.info("-------加载字典信息 end-------");
    }
}

注意点:这种方式在springmvc-spring的项目中使用的时候会出现执行两次的情况。这种是因为在加载spring和springmvc的时候会创建两个容器,都会触发这个事件的执行。这时候只需要在`onApplicationEvent`方法中判断是否有父容器即可。

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){//root application context 没有parent,他就是老大.
            //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。
        }
}

总结

在项目启动时执行代码的方式,以上几种方式在网络大搜集之后整合而来,方便大家查阅。


————————————————
版权声明:本文为CSDN博主「皓月之明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sheinenggaosuwo/article/details/106222181

上一篇:非springboot java单元测试基类


下一篇:logger的日志笔记