初始化执行操作

通常的我们的项目开发中,经常会遇到那种在服务一启动就需要自动执行一些业务代码的情况。比如将数据库中的配置信息或者数据字典之类的缓存到redis,或者在服务启动的时候将一些配置化的定时任务开起来。关于spring mvc或者springboot如何在项目启动的时候就执行一些代码

@PostConstruct注解

从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。@PostConstruct会在所在类的构造函数执行之后执行,在init()方法执行之前执行。(@PreDestroy注解的方法会在这个类的destory()方法执行之后执行。)
@PostConstruct
不是spring提供的而是Java自己的注解。

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct注释用于需要依赖注入完成后才能执行任何初始化的方法上。 在类投入使用之前,必须调用此方法

在使用spring框架时,在一个类内,如果有构造器(Constructor ),有@PostConstruct,还有@Autowired,他们的先后执行顺序为Constructor >> @Autowired >> @PostConstruct。在类加载的时候,为当前类初始化一些数据,那么可以使用@PostConstruct注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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());
}
}
}
}

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

其实有两个接口ApplicationRunner,CommandLineRunner

这两个接口中有一个run方法,只需要实现这个方法即可。这两个接口的不同之处在于:ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组。
:::tip
注意:一定要有@Component这个注解。要不然SpringBoot扫描不到这个类,* 是不会执行。

@Order注解
如果有多个实现类,而需要他们按一定顺序执行的话,可以在实现类上加上@Order注解。@Order(value=整数值)。SpringBoot会按照@Order中的value值从小到大依次执行。
:::
例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@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());
}
}
}
}

使用监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73



/**
* 初始化常量的监听器
* <p>
* 当spring装配好配置后,就去数据库读constants
* <p>等待解决</p>
* <p>
* <a href="https://docs.spring.io/spring-boot/docs/2.7.0/reference/htmlsingle/#features.spring-application.application-events-and-listeners">链接</a>
* </p>
*
* @author yanni
*/

public class ConstantsInitListener implements ApplicationListener<ApplicationContextInitializedEvent>, Ordered {

private static final Log log = Log.get();

private static final String initSql = "select config_value from tb_blog_config where config_field = 'init'";

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}

@Override
public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
ConfigurableEnvironment environment = applicationContextInitializedEvent.getApplicationContext().getEnvironment();

// 获取数据库连接配置
String dataSourceUrl = environment.getProperty("spring.datasource.url");
String dataSourceUsername = environment.getProperty("spring.datasource.username");
String dataSourcePassword = environment.getProperty("spring.datasource.password");

// 如果有为空的配置,终止执行
if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword)) {
try {
throw new Exception("database not cononnected");
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Connection conn = null;
try {
Class.forName("org.postgresql.Driver");
assert dataSourceUrl != null;
conn = DriverManager.getConnection(dataSourceUrl, dataSourceUsername, dataSourcePassword);
Entity init = SqlExecutor.query(conn, initSql, new EntityHandler());
if (init == null) {
SqlExecutor.execute(conn,SqlConstant.initAdminSql );
SqlExecutor.execute(conn, SqlConstant.insertConfigDataSql,new Object() );
SqlExecutor.execute(conn,SqlConstant.initTagSql );
SqlExecutor.execute(conn,SqlConstant.initCateSql );
SqlExecutor.execute(conn,SqlConstant.initLinkSql );
log.info("执行sql成功!");
} else {
log.info("数据已经存在");
}
// 获取sys_config表的数据

} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
log.error(">>> 读取数据库constants配置信息出错:{}", e.getMessage());

} finally {
DbUtil.close(conn);
}

}
}

然后在resources/META-INF/spring.factories写下

1
2
org.springframework.context.ApplicationListener=\
com.site.blog.config.listener.ConstantsInitListener

就可以在启动前写入sql

Buy me a coffee
静听、唯美殇 微信 微信
静听、唯美殇 支付宝 支付宝