开发者

SpringBoot启动时执行特定代码的10种方式

开发者 https://www.devze.com 2025-10-17 10:31 出处:网络 作者: 北辰alk
目录1. 引言2. 启动时执行代码的应用场景2.1 常见应用场景2.2 需求分类3. 方法一:@PostConstruct 注解3.1 基本使用3.2 执行时机和特点4. 方法二:InitializingBean 接口4.1 基本使用4.2 执行顺序5. 方法三:@Bean 的
目录
  • 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 常见应用场景

    1. 数据初始化:数据库表结构初始化、基础数据导入
    2. 缓存预热:加载热点数据到缓存中
    3. 配置验证:检查外部配置的正确性 
    4. 服务注册:向注册中心注册服务实例
    5. 连接建立:建立数据库连接池、Redis 连接等
    6. 文件检查:检查必要的配置文件或资源文件
    7. 定时任务启动:初始化并启动定时任务
    8. 全局变量初始化:初始化应用级别的全局变量

    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 注意事项

    1. 避免循环依赖:在初始化阶段特别注意Bean之间的依赖关系
    2. 配置验证:尽早验证配置的正确性
    3. 资源清理:对于需要清理的资源,实现相应的销毁方法
    4. 日志记录:详细记录初始化过程,便于问题排查

    16. 总结

    本文详细介绍了 Spring Boot 中 10 种在启动时执行特定代码的方式,涵盖了从简单的注解到复杂的事件监听等各种场景。每种方式都有其特定的使用场景和执行时机,在实际项目中应该根据具体需求选择合适的方式。

    通过合理使用这些初始化机制,可以确保应用在启动时完成所有必要的准备工作,为应用的稳定运行奠定坚实的基础。

    以上就是SpringBoot启动时执行特定代码的10种方式的详细内容,更多关于SpringBoot启动时执行特定代码的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号