前言

通常的我们的项目开发中,经常会遇到那种在服务一启动就需要自动执行一些业务代码的情况。比如将数据库中的配置信息或者数据字典之类的缓存到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() {

Listlist = 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) {

Listlist = 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()));

Setkeys = 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{

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容器初始化完成后就会执行该方法。

}

}

总结

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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐