1. 引言
在实际的 spring boot 项目开发中,我们经常需要在应用启动时执行一些特定的初始化代码,比如缓存预热、数据初始化、配置加载、服务注册等。spring boot 提供了多种灵活的方式来实现启动时执行代码的需求。
本文将全面深入地介绍 spring boot 中启动时执行特定代码的 10 种方式,涵盖从简单到复杂的各种场景,并通过详细的代码示例、执行顺序分析和流程图帮助读者彻底掌握这一重要技术。
2. 启动时执行代码的应用场景
2.1 常见应用场景
- 数据初始化:数据库表结构初始化、基础数据导入
- 缓存预热:加载热点数据到缓存中
- 配置验证:检查外部配置的正确性
- 服务注册:向注册中心注册服务实例
- 连接建立:建立数据库连接池、redis 连接等
- 文件检查:检查必要的配置文件或资源文件
- 定时任务启动:初始化并启动定时任务
- 全局变量初始化:初始化应用级别的全局变量
2.2 需求分类
| 需求类型 | 执行时机 | 典型实现方式 |
|---|---|---|
| bean初始化后 | bean创建完成后 | @postconstruct |
| 应用启动后 | 应用完全启动后 | applicationrunner |
| 特定条件初始化 | 满足条件时执行 | @conditional + @bean |
| 顺序执行 | 多个任务按顺序执行 | @order注解 |
3. 方法一:@postconstruct 注解
3.1 基本使用
@postconstruct 是 jsr-250 规范中的注解,用于标记在 bean 初始化完成后执行的方法。
@component
public class postconstructexample {
private static final logger logger = loggerfactory.getlogger(postconstructexample.class);
@postconstruct
public void init() {
logger.info("@postconstruct方法执行 - 开始初始化工作");
// 执行初始化逻辑
initializecache();
loadconfiguration();
validateenvironment();
logger.info("@postconstruct方法执行 - 初始化完成");
}
private void initializecache() {
logger.info("初始化缓存数据...");
// 模拟缓存预热
try {
thread.sleep(1000);
} catch (interruptedexception e) {
thread.currentthread().interrupt();
}
}
private void loadconfiguration() {
logger.info("加载配置文件...");
// 模拟配置加载
}
private void validateenvironment() {
logger.info("验证环境配置...");
// 模拟环境验证
}
}
3.2 执行时机和特点
执行时机:
- 在依赖注入完成之后
- 在
@bean的initmethod之前 - 在 bean 的生命周期早期
特点:
- 执行在 bean 的初始化阶段
- 只执行一次
- 不能有参数
- 不能有返回值
- 可以抛出异常(会阻止 bean 的创建)
4. 方法二:initializingbean 接口
4.1 基本使用
initializingbean 接口提供了一个 afterpropertiesset() 方法,在 bean 的属性设置完成后执行。
@component
public class initializingbeanexample implements initializingbean {
private static final logger logger = loggerfactory.getlogger(initializingbeanexample.class);
private final environment environment;
private string appname;
private string appversion;
public initializingbeanexample(environment environment) {
this.environment = environment;
}
@override
public void afterpropertiesset() throws exception {
logger.info("initializingbean.afterpropertiesset()方法执行");
// 获取配置信息
this.appname = environment.getproperty("spring.application.name", "unknown");
this.appversion = environment.getproperty("app.version", "1.0.0");
// 执行初始化逻辑
initializeapplication();
setupglobalvariables();
logger.info("应用初始化完成: {} v{}", appname, appversion);
}
private void initializeapplication() {
logger.info("初始化应用核心组件...");
// 模拟核心组件初始化
}
private void setupglobalvariables() {
logger.info("设置全局变量...");
// 模拟全局变量设置
}
public string getappinfo() {
return string.format("%s v%s", appname, appversion);
}
}
4.2 执行顺序
@component
public class executionorderexample implements initializingbean {
@postconstruct
public void postconstruct() {
system.out.println("1. @postconstruct 执行");
}
@override
public void afterpropertiesset() throws exception {
system.out.println("2. initializingbean.afterpropertiesset() 执行");
}
@bean(initmethod = "initmethod")
public demobean demobean() {
return new demobean();
}
public static class demobean {
public void initmethod() {
system.out.println("3. @bean initmethod 执行");
}
}
}
5. 方法三:@bean 的 initmethod 属性
5.1 基本使用
在 @bean 注解中指定 initmethod 属性,可以在 bean 初始化完成后执行指定的方法。
@configuration
public class initmethodconfig {
private static final logger logger = loggerfactory.getlogger(initmethodconfig.class);
@bean(initmethod = "initialize", destroymethod = "cleanup")
public databaseconnection databaseconnection() {
logger.info("创建databaseconnection bean实例");
return new databaseconnection();
}
public static class databaseconnection {
public void initialize() {
logger.info("databaseconnection initmethod执行 - 建立数据库连接");
// 模拟数据库连接建立
try {
thread.sleep(2000);
logger.info("数据库连接建立成功");
} catch (interruptedexception e) {
thread.currentthread().interrupt();
logger.error("数据库连接建立失败", e);
}
}
public void cleanup() {
logger.info("databaseconnection destroymethod执行 - 关闭数据库连接");
// 模拟资源清理
}
public void query(string sql) {
logger.info("执行查询: {}", sql);
}
}
}
5.2 复杂示例
@configuration
public class complexinitmethodexample {
@bean(initmethod = "start", destroymethod = "stop")
public messagequeueconsumer messagequeueconsumer() {
return new messagequeueconsumer();
}
@bean(initmethod = "initializeconnectionpool")
public connectionpool connectionpool() {
return new connectionpool();
}
public static class messagequeueconsumer {
public void start() {
system.out.println("消息队列消费者启动...");
// 模拟启动逻辑
}
public void stop() {
system.out.println("消息队列消费者停止...");
// 模拟停止逻辑
}
}
public static class connectionpool {
public void initializeconnectionpool() {
system.out.println("初始化数据库连接池...");
// 模拟连接池初始化
}
}
}
6. 方法四:applicationrunner 接口
6.1 基本使用
applicationrunner 接口在 spring boot 应用完全启动后执行,可以访问 applicationarguments。
@component
@order(1)
public class primaryapplicationrunner implements applicationrunner {
private static final logger logger = loggerfactory.getlogger(primaryapplicationrunner.class);
private final environment environment;
private final userrepository userrepository;
public primaryapplicationrunner(environment environment, userrepository userrepository) {
this.environment = environment;
this.userrepository = userrepository;
}
@override
public void run(applicationarguments args) throws exception {
logger.info("primaryapplicationrunner开始执行 - 应用启动完成");
// 获取启动参数
logger.info("启动参数: {}", arrays.tostring(args.getsourceargs()));
logger.info("非选项参数: {}", args.getnonoptionargs());
logger.info("选项参数名称: {}", args.getoptionnames());
// 执行核心初始化逻辑
initializesystem();
preloaddata();
startbackgroundservices();
logger.info("primaryapplicationrunner执行完成");
}
private void initializesystem() {
logger.info("初始化系统核心组件...");
// 检查数据库连接
checkdatabaseconnection();
// 验证必要的配置
validaterequiredconfigurations();
// 初始化系统缓存
initializesystemcache();
}
private void checkdatabaseconnection() {
try {
long usercount = userrepository.count();
logger.info("数据库连接正常,用户数量: {}", usercount);
} catch (exception e) {
logger.error("数据库连接检查失败", e);
throw new runtimeexception("数据库连接失败", e);
}
}
private void validaterequiredconfigurations() {
string[] requiredprops = {
"spring.datasource.url",
"spring.redis.host",
"app.security.secret-key"
};
for (string prop : requiredprops) {
if (!environment.containsproperty(prop)) {
throw new illegalstateexception("必要的配置项缺失: " + prop);
}
}
logger.info("所有必要配置项验证通过");
}
private void initializesystemcache() {
logger.info("初始化系统缓存...");
// 模拟缓存初始化
}
private void preloaddata() {
logger.info("预加载热点数据...");
// 模拟数据预加载
}
private void startbackgroundservices() {
logger.info("启动后台服务...");
// 模拟后台服务启动
}
}
6.2 多个 applicationrunner 的执行顺序
@component
@order(1)
public class firstapplicationrunner implements applicationrunner {
@override
public void run(applicationarguments args) throws exception {
system.out.println("第一个applicationrunner执行 - order(1)");
// 执行最高优先级的初始化任务
}
}
@component
@order(2)
public class secondapplicationrunner implements applicationrunner {
@override
public void run(applicationarguments args) throws exception {
system.out.println("第二个applicationrunner执行 - order(2)");
// 执行次优先级的初始化任务
}
}
@component
@order(3)
public class thirdapplicationrunner implements applicationrunner {
@override
public void run(applicationarguments args) throws exception {
system.out.println("第三个applicationrunner执行 - order(3)");
// 执行最低优先级的初始化任务
}
}
7. 方法五:commandlinerunner 接口
7.1 基本使用
commandlinerunner 接口与 applicationrunner 类似,但接收的是原始的字符串数组参数。
@component
@order(2)
public class commandlinerunnerexample implements commandlinerunner {
private static final logger logger = loggerfactory.getlogger(commandlinerunnerexample.class);
private final datasource datasource;
private final redistemplate<string, object> redistemplate;
public commandlinerunnerexample(datasource datasource, redistemplate<string, object> redistemplate) {
this.datasource = datasource;
this.redistemplate = redistemplate;
}
@override
public void run(string... args) throws exception {
logger.info("commandlinerunner开始执行 - 参数数量: {}", args.length);
// 输出所有命令行参数
for (int i = 0; i < args.length; i++) {
logger.info("参数 {}: {}", i, args[i]);
}
// 执行数据源相关的初始化
initializedatasource();
// 执行缓存相关的初始化
initializecache();
// 执行其他初始化任务
performsanitychecks();
logger.info("commandlinerunner执行完成");
}
private void initializedatasource() {
logger.info("初始化数据源...");
try {
// 测试数据库连接
try (connection connection = datasource.getconnection()) {
databasemetadata metadata = connection.getmetadata();
logger.info("数据库连接成功: {} {}",
metadata.getdatabaseproductname(),
metadata.getdatabaseproductversion());
}
} catch (sqlexception e) {
logger.error("数据源初始化失败", e);
throw new runtimeexception("数据源初始化失败", e);
}
}
private void initializecache() {
logger.info("初始化缓存...");
try {
// 测试redis连接
redistemplate.opsforvalue().set("startup-test", "success", duration.ofseconds(30));
string result = (string) redistemplate.opsforvalue().get("startup-test");
logger.info("redis连接测试: {}", "success".equals(result) ? "成功" : "失败");
} catch (exception e) {
logger.error("缓存初始化失败", e);
throw new runtimeexception("缓存初始化失败", e);
}
}
private void performsanitychecks() {
logger.info("执行系统健康检查...");
// 模拟系统检查
checkdiskspace();
checkmemoryusage();
}
private void checkdiskspace() {
logger.info("检查磁盘空间...");
// 模拟磁盘空间检查
}
private void checkmemoryusage() {
logger.info("检查内存使用情况...");
// 模拟内存检查
}
}
8. 方法六:@eventlistener 监听 contextrefreshedevent
8.1 基本使用
通过监听 spring 上下文刷新完成事件来执行初始化代码。
@component
public class contextrefreshedeventlistener {
private static final logger logger = loggerfactory.getlogger(contextrefreshedeventlistener.class);
private final applicationcontext applicationcontext;
private final systemconfigservice systemconfigservice;
public contextrefreshedeventlistener(applicationcontext applicationcontext,
systemconfigservice systemconfigservice) {
this.applicationcontext = applicationcontext;
this.systemconfigservice = systemconfigservice;
}
@eventlistener(contextrefreshedevent.class)
public void oncontextrefreshed(contextrefreshedevent event) {
// 避免在子容器中重复执行
if (event.getapplicationcontext().getparent() != null) {
return;
}
logger.info("contextrefreshedevent事件触发 - spring容器刷新完成");
// 执行初始化逻辑
loadsystemconfigurations();
initializethirdpartyservices();
registersystembeans();
logger.info("contextrefreshedevent事件处理完成");
}
private void loadsystemconfigurations() {
logger.info("加载系统配置...");
try {
// 从数据库加载系统配置
map<string, string> configs = systemconfigservice.loadallconfigs();
logger.info("加载了 {} 条系统配置", configs.size());
// 将配置设置到应用上下文中
configs.foreach((key, value) -> {
logger.debug("系统配置: {} = {}", key, value);
});
} catch (exception e) {
logger.error("系统配置加载失败", e);
throw new runtimeexception("系统配置加载失败", e);
}
}
private void initializethirdpartyservices() {
logger.info("初始化第三方服务...");
// 检查所有需要的bean是否已正确初始化
string[] requiredbeans = {
"datasource",
"redistemplate",
"resttemplate"
};
for (string beanname : requiredbeans) {
if (applicationcontext.containsbean(beanname)) {
object bean = applicationcontext.getbean(beanname);
logger.info("bean {} 初始化成功: {}", beanname, bean.getclass().getsimplename());
} else {
logger.warn("bean {} 未找到", beanname);
}
}
}
private void registersystembeans() {
logger.info("注册系统bean...");
// 模拟系统bean注册
}
}
8.2 其他相关事件
@component
public class multipleeventlisteners {
private static final logger logger = loggerfactory.getlogger(multipleeventlisteners.class);
/**
* 应用启动事件
*/
@eventlistener(applicationstartedevent.class)
public void onapplicationstarted(applicationstartedevent event) {
logger.info("应用程序启动完成 - applicationstartedevent");
}
/**
* 应用准备就绪事件
*/
@eventlistener(applicationreadyevent.class)
public void onapplicationready(applicationreadyevent event) {
logger.info("应用程序准备就绪 - applicationreadyevent");
// 可以在这里执行一些需要在应用完全就绪后执行的任务
}
/**
* 应用启动失败事件
*/
@eventlistener(applicationfailedevent.class)
public void onapplicationfailed(applicationfailedevent event) {
logger.error("应用程序启动失败 - applicationfailedevent", event.getexception());
// 可以在这里执行清理操作或发送告警
}
}
9. 方法七:spring boot 的 applicationlistener
9.1 基本使用
实现 applicationlistener 接口来监听特定的事件。
@component
public class customapplicationlistener implements applicationlistener<applicationreadyevent> {
private static final logger logger = loggerfactory.getlogger(customapplicationlistener.class);
private final metricservice metricservice;
private final healthcheckservice healthcheckservice;
public customapplicationlistener(metricservice metricservice,
healthcheckservice healthcheckservice) {
this.metricservice = metricservice;
this.healthcheckservice = healthcheckservice;
}
@override
public void onapplicationevent(applicationreadyevent event) {
logger.info("applicationlistener收到applicationreadyevent - 开始执行启动任务");
// 执行系统健康检查
performhealthchecks();
// 初始化监控指标
initializemetrics();
// 启动后台监控任务
startmonitoringtasks();
logger.info("applicationlistener启动任务执行完成");
}
private void performhealthchecks() {
logger.info("执行系统健康检查...");
try {
healthcheckresult result = healthcheckservice.performcomprehensivecheck();
if (result.ishealthy()) {
logger.info("系统健康检查通过: {}", result.getmessage());
} else {
logger.error("系统健康检查失败: {}", result.getmessage());
// 可以根据严重程度决定是否终止启动
if (result.iscritical()) {
throw new runtimeexception("关键健康检查失败: " + result.getmessage());
}
}
} catch (exception e) {
logger.error("健康检查执行异常", e);
throw new runtimeexception("健康检查失败", e);
}
}
private void initializemetrics() {
logger.info("初始化系统监控指标...");
// 注册自定义指标
metricservice.registercustommetrics();
// 初始化性能基准
metricservice.initializeperformancebaselines();
logger.info("监控指标初始化完成");
}
private void startmonitoringtasks() {
logger.info("启动后台监控任务...");
// 启动性能监控
metricservice.startperformancemonitoring();
// 启动资源使用监控
metricservice.startresourcemonitoring();
logger.info("后台监控任务启动完成");
}
}
10. 方法八:@configuration 类的静态代码块
10.1 基本使用
在配置类中使用静态代码块在类加载时执行代码。
@configuration
public class staticblockconfiguration {
private static final logger logger = loggerfactory.getlogger(staticblockconfiguration.class);
// 静态代码块 - 在类加载时执行
static {
logger.info("staticblockconfiguration类加载 - 静态代码块执行");
// 执行一些类级别的初始化
initializenativelibraries();
setupsecurityproviders();
configurelogging();
}
private static void initializenativelibraries() {
logger.info("初始化本地库...");
// 模拟本地库初始化
try {
// 加载本地库
// system.loadlibrary("native-lib");
logger.info("本地库初始化完成");
} catch (exception e) {
logger.error("本地库初始化失败", e);
}
}
private static void setupsecurityproviders() {
logger.info("设置安全提供者...");
// 模拟安全设置
}
private static void configurelogging() {
logger.info("配置日志系统...");
// 模拟日志配置
}
@bean
public systeminitializer systeminitializer() {
logger.info("创建systeminitializer bean");
return new systeminitializer();
}
public static class systeminitializer {
public systeminitializer() {
logger.info("systeminitializer构造函数执行");
}
}
}
11. 方法九:smartlifecycle 接口
11.1 基本使用
smartlifecycle 接口提供了更精细的生命周期控制。
@component
public class customsmartlifecycle implements smartlifecycle {
private static final logger logger = loggerfactory.getlogger(customsmartlifecycle.class);
private volatile boolean running = false;
private final taskscheduler taskscheduler;
private scheduledfuture<?> scheduledtask;
public customsmartlifecycle(taskscheduler taskscheduler) {
this.taskscheduler = taskscheduler;
}
@override
public void start() {
logger.info("customsmartlifecycle开始启动");
if (!running) {
// 执行启动逻辑
initializeservices();
startbackgroundtasks();
running = true;
logger.info("customsmartlifecycle启动完成");
}
}
@override
public void stop() {
logger.info("customsmartlifecycle开始停止");
if (running) {
// 执行停止逻辑
stopbackgroundtasks();
cleanupresources();
running = false;
logger.info("customsmartlifecycle停止完成");
}
}
@override
public boolean isrunning() {
return running;
}
@override
public int getphase() {
// 返回一个相位值,控制启动和停止的顺序
return 0;
}
@override
public boolean isautostartup() {
return true;
}
private void initializeservices() {
logger.info("初始化后台服务...");
// 模拟服务初始化
}
private void startbackgroundtasks() {
logger.info("启动后台任务...");
// 启动定时任务
scheduledtask = taskscheduler.scheduleatfixedrate(() -> {
logger.debug("执行后台任务...");
// 模拟后台任务执行
}, duration.ofseconds(30));
}
private void stopbackgroundtasks() {
logger.info("停止后台任务...");
if (scheduledtask != null && !scheduledtask.iscancelled()) {
scheduledtask.cancel(true);
logger.info("后台任务已停止");
}
}
private void cleanupresources() {
logger.info("清理资源...");
// 模拟资源清理
}
}
12. 方法十:@conditional 条件化初始化
12.1 基本使用
根据条件决定是否执行初始化逻辑。
@configuration
public class conditionalinitializationconfig {
private static final logger logger = loggerfactory.getlogger(conditionalinitializationconfig.class);
@bean
@conditionalonproperty(name = "app.feature.cache.enabled", havingvalue = "true")
public cacheinitializer cacheinitializer() {
logger.info("创建cacheinitializer - 缓存功能已启用");
return new cacheinitializer();
}
@bean
@conditionalonclass(name = "com.example.externalservice")
public externalserviceinitializer externalserviceinitializer() {
logger.info("创建externalserviceinitializer - 检测到externalservice类");
return new externalserviceinitializer();
}
@bean
@conditionalonexpression("${app.mode:dev} == 'prod'")
public productioninitializer productioninitializer() {
logger.info("创建productioninitializer - 生产环境模式");
return new productioninitializer();
}
@bean
@conditionalonwebapplication
public webapplicationinitializer webapplicationinitializer() {
logger.info("创建webapplicationinitializer - web应用环境");
return new webapplicationinitializer();
}
public static class cacheinitializer {
@postconstruct
public void init() {
logger.info("初始化缓存系统...");
// 模拟缓存初始化
}
}
public static class externalserviceinitializer {
@postconstruct
public void init() {
logger.info("初始化外部服务...");
// 模拟外部服务初始化
}
}
public static class productioninitializer {
@postconstruct
public void init() {
logger.info("执行生产环境特定初始化...");
// 生产环境特定的初始化逻辑
}
}
public static class webapplicationinitializer {
@postconstruct
public void init() {
logger.info("执行web应用特定初始化...");
// web应用特定的初始化逻辑
}
}
}
13. 执行顺序和优先级
13.1 完整的执行顺序流程图
graph td
a[spring boot应用启动] --> b[加载配置类静态代码块]
b --> c[创建bean实例]
c --> d[依赖注入]
d --> e[@postconstruct方法]
e --> f[initializingbean.afterpropertiesset]
f --> g[@bean initmethod]
g --> h[contextrefreshedevent事件]
h --> i[applicationrunner.run]
i --> j[commandlinerunner.run]
j --> k[applicationreadyevent事件]
k --> l[应用完全就绪]
style b fill:#e1f5fe
style e fill:#f3e5f5
style f fill:#e8f5e8
style g fill:#fff3e0
style i fill:#ffebee
style j fill:#fce4ec
13.2 执行顺序验证代码
@component
public class executionorderverifier {
// 1. 静态代码块(类加载时执行)
static {
system.out.println("1. 静态代码块执行");
}
public executionorderverifier() {
system.out.println("2. 构造函数执行");
}
@postconstruct
public void postconstruct() {
system.out.println("3. @postconstruct方法执行");
}
}
@component
public class executionorderverifier2 implements initializingbean {
@postconstruct
public void postconstruct() {
system.out.println("4. @postconstruct方法执行(第二个bean)");
}
@override
public void afterpropertiesset() throws exception {
system.out.println("5. initializingbean.afterpropertiesset执行");
}
}
@configuration
public class executionorderconfig {
@bean(initmethod = "initmethod")
public orderdemobean orderdemobean() {
system.out.println("6. @bean方法执行 - 创建bean实例");
return new orderdemobean();
}
public static class orderdemobean {
public void initmethod() {
system.out.println("7. @bean initmethod执行");
}
}
}
@component
@order(1)
public class orderapplicationrunner implements applicationrunner {
@override
public void run(applicationarguments args) throws exception {
system.out.println("8. applicationrunner执行 - order(1)");
}
}
@component
@order(2)
public class ordercommandlinerunner implements commandlinerunner {
@override
public void run(string... args) throws exception {
system.out.println("9. commandlinerunner执行 - order(2)");
}
}
@component
public class ordereventlistener {
@eventlistener(contextrefreshedevent.class)
public void oncontextrefreshed(contextrefreshedevent event) {
system.out.println("10. contextrefreshedevent事件处理");
}
@eventlistener(applicationreadyevent.class)
public void onapplicationready(applicationreadyevent event) {
system.out.println("11. applicationreadyevent事件处理 - 应用完全就绪");
}
}
14. 实际应用案例
14.1 完整的系统初始化器
@component
@order(1)
public class systeminitializer implements applicationrunner {
private static final logger logger = loggerfactory.getlogger(systeminitializer.class);
private final applicationcontext applicationcontext;
private final datasource datasource;
private final redistemplate<string, object> redistemplate;
private final systemconfigrepository configrepository;
public systeminitializer(applicationcontext applicationcontext,
datasource datasource,
redistemplate<string, object> redistemplate,
systemconfigrepository configrepository) {
this.applicationcontext = applicationcontext;
this.datasource = datasource;
this.redistemplate = redistemplate;
this.configrepository = configrepository;
}
@override
public void run(applicationarguments args) throws exception {
logger.info("====== 系统初始化开始 ======");
// 阶段1: 基础设施检查
checkinfrastructure();
// 阶段2: 数据初始化
initializedata();
// 阶段3: 缓存预热
warmupcaches();
// 阶段4: 服务启动
startservices();
logger.info("====== 系统初始化完成 ======");
}
private void checkinfrastructure() {
logger.info("阶段1: 检查基础设施...");
// 检查数据库
checkdatabase();
// 检查redis
checkredis();
// 检查磁盘空间
checkdiskspace();
logger.info("基础设施检查完成");
}
private void checkdatabase() {
try (connection connection = datasource.getconnection()) {
databasemetadata metadata = connection.getmetadata();
string dbname = metadata.getdatabaseproductname();
string version = metadata.getdatabaseproductversion();
logger.info("数据库连接正常: {} {}", dbname, version);
// 检查必要的表是否存在
checkrequiredtables(connection);
} catch (sqlexception e) {
logger.error("数据库检查失败", e);
throw new runtimeexception("数据库不可用", e);
}
}
private void checkrequiredtables(connection connection) throws sqlexception {
string[] requiredtables = {"users", "system_config", "audit_log"};
databasemetadata metadata = connection.getmetadata();
for (string table : requiredtables) {
try (resultset rs = metadata.gettables(null, null, table, null)) {
if (!rs.next()) {
throw new runtimeexception("必要的表不存在: " + table);
}
}
}
logger.info("所有必要的表都存在");
}
private void checkredis() {
try {
redistemplate.opsforvalue().set("health-check", "ok", duration.ofseconds(10));
string result = (string) redistemplate.opsforvalue().get("health-check");
if ("ok".equals(result)) {
logger.info("redis连接正常");
} else {
throw new runtimeexception("redis响应异常");
}
} catch (exception e) {
logger.error("redis检查失败", e);
throw new runtimeexception("redis不可用", e);
}
}
private void checkdiskspace() {
file root = new file("/");
long freespace = root.getfreespace();
long totalspace = root.gettotalspace();
double freepercent = (double) freespace / totalspace * 100;
logger.info("磁盘空间: {}/{} gb ({:.2f}% 空闲)",
(totalspace - freespace) / (1024 * 1024 * 1024),
totalspace / (1024 * 1024 * 1024),
freepercent);
if (freepercent < 10.0) {
logger.warn("磁盘空间不足,空闲空间低于10%");
}
}
private void initializedata() {
logger.info("阶段2: 初始化数据...");
// 加载系统配置
loadsystemconfigs();
// 初始化基础数据
initializebasedata();
logger.info("数据初始化完成");
}
private void loadsystemconfigs() {
try {
list<systemconfig> configs = configrepository.findall();
logger.info("加载了 {} 条系统配置", configs.size());
// 将配置加载到内存或缓存中
configs.foreach(config -> {
logger.debug("系统配置: {} = {}", config.getkey(), config.getvalue());
});
} catch (exception e) {
logger.error("系统配置加载失败", e);
throw new runtimeexception("系统配置加载失败", e);
}
}
private void initializebasedata() {
logger.info("初始化基础数据...");
// 模拟基础数据初始化
// 如:默认用户、角色、权限等
}
private void warmupcaches() {
logger.info("阶段3: 缓存预热...");
// 预热用户缓存
warmupusercache();
// 预热配置缓存
warmupconfigcache();
logger.info("缓存预热完成");
}
private void warmupusercache() {
logger.info("预热用户缓存...");
// 模拟用户缓存预热
}
private void warmupconfigcache() {
logger.info("预热配置缓存...");
// 模拟配置缓存预热
}
private void startservices() {
logger.info("阶段4: 启动服务...");
// 启动定时任务服务
startscheduledtasks();
// 启动消息监听服务
startmessagelisteners();
logger.info("服务启动完成");
}
private void startscheduledtasks() {
logger.info("启动定时任务...");
// 模拟定时任务启动
}
private void startmessagelisteners() {
logger.info("启动消息监听器...");
// 模拟消息监听器启动
}
}
15. 最佳实践和注意事项
15.1 最佳实践
合理选择初始化方式:
- bean初始化使用
@postconstruct - 应用启动后任务使用
applicationrunner - 需要命令行参数的使用
commandlinerunner
控制初始化顺序:
- 使用
@order注解控制多个 runner 的执行顺序 - 理解不同初始化方式的执行时机
异常处理:
- 初始化失败应该快速失败
- 提供清晰的错误信息
- 区分关键初始化和非关键初始化
性能考虑:
- 避免在初始化阶段执行耗时操作
- 长时间任务应该异步执行
- 考虑使用懒加载
15.2 注意事项
- 避免循环依赖:在初始化阶段特别注意bean之间的依赖关系
- 配置验证:尽早验证配置的正确性
- 资源清理:对于需要清理的资源,实现相应的销毁方法
- 日志记录:详细记录初始化过程,便于问题排查
16. 总结
本文详细介绍了 spring boot 中 10 种在启动时执行特定代码的方式,涵盖了从简单的注解到复杂的事件监听等各种场景。每种方式都有其特定的使用场景和执行时机,在实际项目中应该根据具体需求选择合适的方式。
通过合理使用这些初始化机制,可以确保应用在启动时完成所有必要的准备工作,为应用的稳定运行奠定坚实的基础。
以上就是springboot启动时执行特定代码的10种方式的详细内容,更多关于springboot启动时执行特定代码的资料请关注代码网其它相关文章!
发表评论