目录
- 1. 引言
- 2. 启动时执行代码的应用场景
- 2.1 常见应用场景
- 2.2 需求分类
- 3. 方法一:@PostConstruct 注解
- 3.1 基本使用
- 3.2 执行时机和特点
- 4. 方法二:InitializingBean 接口
- 4.1 基本使用
- 4.2 执行顺序
- 5. 方法三:@Bean 的 initMethod 属性
- 5.1 基本使用
- 5.2 复杂示例
- 6. 方法四:ApplicationRunner 接口
- 6.1 基本使用
- 6.2 多个 ApplicationRunner 的执行顺序
- 7. 方法五:CommandLineRunner 接口
- 7.1 基本使用
- 8. 方法六:@EventListener 监听 ContextRefreshedEvent
- 8.1 基本使用
- 8.2 其他相关事件
- 9. 方法七:Spring Boot 的 ApplicationListener
- 9.1 基本使用
- 10. 方法八:@Configuration 类的静态代码块
- 10.1 基本使用
- 11. 方法九:SmartLifecycle 接口
- 11.1 基本使用
- 12. 方法十:@Conditional 条件化初始化
- 12.1 基本使用
- 13. 执行顺序和优先级
- 13.1 完整的执行顺序流程图
- 13.2 执行顺序验证代码
- 14. 实际应用案例
- 14.1 完整的系统初始化器
- 15. 最佳实践和注意事项
- 15.1 最佳实践
- 15.2 注意事项
- 16. 总结
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 { publihttp://www.devze.comc 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.inf编程o("基础设施检查完成"); } 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启动时执行特定代码的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论