亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot啟動時執(zhí)行特定代碼的10種方式

 更新時間:2025年10月17日 09:19:03   作者:北辰alk  
在實際的 Spring Boot 項目開發(fā)中,我們經(jīng)常需要在應(yīng)用啟動時執(zhí)行一些特定的初始化代碼,本文將全面深入地介紹 Spring Boot 中啟動時執(zhí)行特定代碼的 10 種方式,需要的朋友可以參考下

1. 引言

在實際的 Spring Boot 項目開發(fā)中,我們經(jīng)常需要在應(yīng)用啟動時執(zhí)行一些特定的初始化代碼,比如緩存預(yù)熱、數(shù)據(jù)初始化、配置加載、服務(wù)注冊等。Spring Boot 提供了多種靈活的方式來實現(xiàn)啟動時執(zhí)行代碼的需求。

本文將全面深入地介紹 Spring Boot 中啟動時執(zhí)行特定代碼的 10 種方式,涵蓋從簡單到復(fù)雜的各種場景,并通過詳細的代碼示例、執(zhí)行順序分析和流程圖幫助讀者徹底掌握這一重要技術(shù)。

2. 啟動時執(zhí)行代碼的應(yīng)用場景

2.1 常見應(yīng)用場景

  1. 數(shù)據(jù)初始化:數(shù)據(jù)庫表結(jié)構(gòu)初始化、基礎(chǔ)數(shù)據(jù)導(dǎo)入
  2. 緩存預(yù)熱:加載熱點數(shù)據(jù)到緩存中
  3. 配置驗證:檢查外部配置的正確性 
  4. 服務(wù)注冊:向注冊中心注冊服務(wù)實例
  5. 連接建立:建立數(shù)據(jù)庫連接池、Redis 連接等
  6. 文件檢查:檢查必要的配置文件或資源文件
  7. 定時任務(wù)啟動:初始化并啟動定時任務(wù)
  8. 全局變量初始化:初始化應(yīng)用級別的全局變量

2.2 需求分類

需求類型執(zhí)行時機典型實現(xiàn)方式
Bean初始化后Bean創(chuàng)建完成后@PostConstruct
應(yīng)用啟動后應(yīng)用完全啟動后ApplicationRunner
特定條件初始化滿足條件時執(zhí)行@Conditional + @Bean
順序執(zhí)行多個任務(wù)按順序執(zhí)行@Order注解

3. 方法一:@PostConstruct 注解

3.1 基本使用

@PostConstruct 是 JSR-250 規(guī)范中的注解,用于標記在 Bean 初始化完成后執(zhí)行的方法。

@Component
public class PostConstructExample {
    
    private static final Logger logger = LoggerFactory.getLogger(PostConstructExample.class);
    
    @PostConstruct
    public void init() {
        logger.info("@PostConstruct方法執(zhí)行 - 開始初始化工作");
        
        // 執(zhí)行初始化邏輯
        initializeCache();
        loadConfiguration();
        validateEnvironment();
        
        logger.info("@PostConstruct方法執(zhí)行 - 初始化完成");
    }
    
    private void initializeCache() {
        logger.info("初始化緩存數(shù)據(jù)...");
        // 模擬緩存預(yù)熱
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private void loadConfiguration() {
        logger.info("加載配置文件...");
        // 模擬配置加載
    }
    
    private void validateEnvironment() {
        logger.info("驗證環(huán)境配置...");
        // 模擬環(huán)境驗證
    }
}

3.2 執(zhí)行時機和特點

執(zhí)行時機

  • 在依賴注入完成之后
  • 在 @Bean 的 initMethod 之前
  • 在 Bean 的生命周期早期

特點

  • 執(zhí)行在 Bean 的初始化階段
  • 只執(zhí)行一次
  • 不能有參數(shù)
  • 不能有返回值
  • 可以拋出異常(會阻止 Bean 的創(chuàng)建)

4. 方法二:InitializingBean 接口

4.1 基本使用

InitializingBean 接口提供了一個 afterPropertiesSet() 方法,在 Bean 的屬性設(shè)置完成后執(zhí)行。

@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()方法執(zhí)行");
        
        // 獲取配置信息
        this.appName = environment.getProperty("spring.application.name", "unknown");
        this.appVersion = environment.getProperty("app.version", "1.0.0");
        
        // 執(zhí)行初始化邏輯
        initializeApplication();
        setupGlobalVariables();
        
        logger.info("應(yīng)用初始化完成: {} v{}", appName, appVersion);
    }
    
    private void initializeApplication() {
        logger.info("初始化應(yīng)用核心組件...");
        // 模擬核心組件初始化
    }
    
    private void setupGlobalVariables() {
        logger.info("設(shè)置全局變量...");
        // 模擬全局變量設(shè)置
    }
    
    public String getAppInfo() {
        return String.format("%s v%s", appName, appVersion);
    }
}

4.2 執(zhí)行順序

@Component
public class ExecutionOrderExample implements InitializingBean {
    
    @PostConstruct
    public void postConstruct() {
        System.out.println("1. @PostConstruct 執(zhí)行");
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("2. InitializingBean.afterPropertiesSet() 執(zhí)行");
    }
    
    @Bean(initMethod = "initMethod")
    public DemoBean demoBean() {
        return new DemoBean();
    }
    
    public static class DemoBean {
        public void initMethod() {
            System.out.println("3. @Bean initMethod 執(zhí)行");
        }
    }
}

5. 方法三:@Bean 的 initMethod 屬性

5.1 基本使用

在 @Bean 注解中指定 initMethod 屬性,可以在 Bean 初始化完成后執(zhí)行指定的方法。

@Configuration
public class InitMethodConfig {
    
    private static final Logger logger = LoggerFactory.getLogger(InitMethodConfig.class);
    
    @Bean(initMethod = "initialize", destroyMethod = "cleanup")
    public DatabaseConnection databaseConnection() {
        logger.info("創(chuàng)建DatabaseConnection Bean實例");
        return new DatabaseConnection();
    }
    
    public static class DatabaseConnection {
        
        public void initialize() {
            logger.info("DatabaseConnection initMethod執(zhí)行 - 建立數(shù)據(jù)庫連接");
            // 模擬數(shù)據(jù)庫連接建立
            try {
                Thread.sleep(2000);
                logger.info("數(shù)據(jù)庫連接建立成功");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.error("數(shù)據(jù)庫連接建立失敗", e);
            }
        }
        
        public void cleanup() {
            logger.info("DatabaseConnection destroyMethod執(zhí)行 - 關(guān)閉數(shù)據(jù)庫連接");
            // 模擬資源清理
        }
        
        public void query(String sql) {
            logger.info("執(zhí)行查詢: {}", sql);
        }
    }
}

5.2 復(fù)雜示例

@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("初始化數(shù)據(jù)庫連接池...");
            // 模擬連接池初始化
        }
    }
}

6. 方法四:ApplicationRunner 接口

6.1 基本使用

ApplicationRunner 接口在 Spring Boot 應(yīng)用完全啟動后執(zhí)行,可以訪問 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開始執(zhí)行 - 應(yīng)用啟動完成");
        
        // 獲取啟動參數(shù)
        logger.info("啟動參數(shù): {}", Arrays.toString(args.getSourceArgs()));
        logger.info("非選項參數(shù): {}", args.getNonOptionArgs());
        logger.info("選項參數(shù)名稱: {}", args.getOptionNames());
        
        // 執(zhí)行核心初始化邏輯
        initializeSystem();
        preloadData();
        startBackgroundServices();
        
        logger.info("PrimaryApplicationRunner執(zhí)行完成");
    }
    
    private void initializeSystem() {
        logger.info("初始化系統(tǒng)核心組件...");
        
        // 檢查數(shù)據(jù)庫連接
        checkDatabaseConnection();
        
        // 驗證必要的配置
        validateRequiredConfigurations();
        
        // 初始化系統(tǒng)緩存
        initializeSystemCache();
    }
    
    private void checkDatabaseConnection() {
        try {
            long userCount = userRepository.count();
            logger.info("數(shù)據(jù)庫連接正常,用戶數(shù)量: {}", userCount);
        } catch (Exception e) {
            logger.error("數(shù)據(jù)庫連接檢查失敗", e);
            throw new RuntimeException("數(shù)據(jù)庫連接失敗", 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("初始化系統(tǒng)緩存...");
        // 模擬緩存初始化
    }
    
    private void preloadData() {
        logger.info("預(yù)加載熱點數(shù)據(jù)...");
        // 模擬數(shù)據(jù)預(yù)加載
    }
    
    private void startBackgroundServices() {
        logger.info("啟動后臺服務(wù)...");
        // 模擬后臺服務(wù)啟動
    }
}

6.2 多個 ApplicationRunner 的執(zhí)行順序

@Component
@Order(1)
public class FirstApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("第一個ApplicationRunner執(zhí)行 - Order(1)");
        // 執(zhí)行最高優(yōu)先級的初始化任務(wù)
    }
}

@Component  
@Order(2)
public class SecondApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("第二個ApplicationRunner執(zhí)行 - Order(2)");
        // 執(zhí)行次優(yōu)先級的初始化任務(wù)
    }
}

@Component
@Order(3)
public class ThirdApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("第三個ApplicationRunner執(zhí)行 - Order(3)");
        // 執(zhí)行最低優(yōu)先級的初始化任務(wù)
    }
}

7. 方法五:CommandLineRunner 接口

7.1 基本使用

CommandLineRunner 接口與 ApplicationRunner 類似,但接收的是原始的字符串?dāng)?shù)組參數(shù)。

@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開始執(zhí)行 - 參數(shù)數(shù)量: {}", args.length);
        
        // 輸出所有命令行參數(shù)
        for (int i = 0; i < args.length; i++) {
            logger.info("參數(shù) {}: {}", i, args[i]);
        }
        
        // 執(zhí)行數(shù)據(jù)源相關(guān)的初始化
        initializeDataSource();
        
        // 執(zhí)行緩存相關(guān)的初始化
        initializeCache();
        
        // 執(zhí)行其他初始化任務(wù)
        performSanityChecks();
        
        logger.info("CommandLineRunner執(zhí)行完成");
    }
    
    private void initializeDataSource() {
        logger.info("初始化數(shù)據(jù)源...");
        try {
            // 測試數(shù)據(jù)庫連接
            try (Connection connection = dataSource.getConnection()) {
                DatabaseMetaData metaData = connection.getMetaData();
                logger.info("數(shù)據(jù)庫連接成功: {} {}", 
                    metaData.getDatabaseProductName(), 
                    metaData.getDatabaseProductVersion());
            }
        } catch (SQLException e) {
            logger.error("數(shù)據(jù)源初始化失敗", e);
            throw new RuntimeException("數(shù)據(jù)源初始化失敗", 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("執(zhí)行系統(tǒng)健康檢查...");
        // 模擬系統(tǒng)檢查
        checkDiskSpace();
        checkMemoryUsage();
    }
    
    private void checkDiskSpace() {
        logger.info("檢查磁盤空間...");
        // 模擬磁盤空間檢查
    }
    
    private void checkMemoryUsage() {
        logger.info("檢查內(nèi)存使用情況...");
        // 模擬內(nèi)存檢查
    }
}

8. 方法六:@EventListener 監(jiān)聽 ContextRefreshedEvent

8.1 基本使用

通過監(jiān)聽 Spring 上下文刷新完成事件來執(zhí)行初始化代碼。

@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) {
        // 避免在子容器中重復(fù)執(zhí)行
        if (event.getApplicationContext().getParent() != null) {
            return;
        }
        
        logger.info("ContextRefreshedEvent事件觸發(fā) - Spring容器刷新完成");
        
        // 執(zhí)行初始化邏輯
        loadSystemConfigurations();
        initializeThirdPartyServices();
        registerSystemBeans();
        
        logger.info("ContextRefreshedEvent事件處理完成");
    }
    
    private void loadSystemConfigurations() {
        logger.info("加載系統(tǒng)配置...");
        
        try {
            // 從數(shù)據(jù)庫加載系統(tǒng)配置
            Map<String, String> configs = systemConfigService.loadAllConfigs();
            logger.info("加載了 {} 條系統(tǒng)配置", configs.size());
            
            // 將配置設(shè)置到應(yīng)用上下文中
            configs.forEach((key, value) -> {
                logger.debug("系統(tǒng)配置: {} = {}", key, value);
            });
            
        } catch (Exception e) {
            logger.error("系統(tǒng)配置加載失敗", e);
            throw new RuntimeException("系統(tǒng)配置加載失敗", e);
        }
    }
    
    private void initializeThirdPartyServices() {
        logger.info("初始化第三方服務(wù)...");
        
        // 檢查所有需要的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("注冊系統(tǒng)Bean...");
        // 模擬系統(tǒng)Bean注冊
    }
}

8.2 其他相關(guān)事件

@Component
public class MultipleEventListeners {
    
    private static final Logger logger = LoggerFactory.getLogger(MultipleEventListeners.class);
    
    /**
     * 應(yīng)用啟動事件
     */
    @EventListener(ApplicationStartedEvent.class)
    public void onApplicationStarted(ApplicationStartedEvent event) {
        logger.info("應(yīng)用程序啟動完成 - ApplicationStartedEvent");
    }
    
    /**
     * 應(yīng)用準備就緒事件
     */
    @EventListener(ApplicationReadyEvent.class)
    public void onApplicationReady(ApplicationReadyEvent event) {
        logger.info("應(yīng)用程序準備就緒 - ApplicationReadyEvent");
        // 可以在這里執(zhí)行一些需要在應(yīng)用完全就緒后執(zhí)行的任務(wù)
    }
    
    /**
     * 應(yīng)用啟動失敗事件
     */
    @EventListener(ApplicationFailedEvent.class)
    public void onApplicationFailed(ApplicationFailedEvent event) {
        logger.error("應(yīng)用程序啟動失敗 - ApplicationFailedEvent", event.getException());
        // 可以在這里執(zhí)行清理操作或發(fā)送告警
    }
}

9. 方法七:Spring Boot 的 ApplicationListener

9.1 基本使用

實現(xiàn) ApplicationListener 接口來監(jiān)聽特定的事件。

@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 - 開始執(zhí)行啟動任務(wù)");
        
        // 執(zhí)行系統(tǒng)健康檢查
        performHealthChecks();
        
        // 初始化監(jiān)控指標
        initializeMetrics();
        
        // 啟動后臺監(jiān)控任務(wù)
        startMonitoringTasks();
        
        logger.info("ApplicationListener啟動任務(wù)執(zhí)行完成");
    }
    
    private void performHealthChecks() {
        logger.info("執(zhí)行系統(tǒng)健康檢查...");
        
        try {
            HealthCheckResult result = healthCheckService.performComprehensiveCheck();
            
            if (result.isHealthy()) {
                logger.info("系統(tǒng)健康檢查通過: {}", result.getMessage());
            } else {
                logger.error("系統(tǒng)健康檢查失敗: {}", result.getMessage());
                // 可以根據(jù)嚴重程度決定是否終止啟動
                if (result.isCritical()) {
                    throw new RuntimeException("關(guān)鍵健康檢查失敗: " + result.getMessage());
                }
            }
        } catch (Exception e) {
            logger.error("健康檢查執(zhí)行異常", e);
            throw new RuntimeException("健康檢查失敗", e);
        }
    }
    
    private void initializeMetrics() {
        logger.info("初始化系統(tǒng)監(jiān)控指標...");
        
        // 注冊自定義指標
        metricService.registerCustomMetrics();
        
        // 初始化性能基準
        metricService.initializePerformanceBaselines();
        
        logger.info("監(jiān)控指標初始化完成");
    }
    
    private void startMonitoringTasks() {
        logger.info("啟動后臺監(jiān)控任務(wù)...");
        
        // 啟動性能監(jiān)控
        metricService.startPerformanceMonitoring();
        
        // 啟動資源使用監(jiān)控
        metricService.startResourceMonitoring();
        
        logger.info("后臺監(jiān)控任務(wù)啟動完成");
    }
}

10. 方法八:@Configuration 類的靜態(tài)代碼塊

10.1 基本使用

在配置類中使用靜態(tài)代碼塊在類加載時執(zhí)行代碼。

@Configuration
public class StaticBlockConfiguration {
    
    private static final Logger logger = LoggerFactory.getLogger(StaticBlockConfiguration.class);
    
    // 靜態(tài)代碼塊 - 在類加載時執(zhí)行
    static {
        logger.info("StaticBlockConfiguration類加載 - 靜態(tài)代碼塊執(zhí)行");
        
        // 執(zhí)行一些類級別的初始化
        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("設(shè)置安全提供者...");
        // 模擬安全設(shè)置
    }
    
    private static void configureLogging() {
        logger.info("配置日志系統(tǒng)...");
        // 模擬日志配置
    }
    
    @Bean
    public SystemInitializer systemInitializer() {
        logger.info("創(chuàng)建SystemInitializer Bean");
        return new SystemInitializer();
    }
    
    public static class SystemInitializer {
        public SystemInitializer() {
            logger.info("SystemInitializer構(gòu)造函數(shù)執(zhí)行");
        }
    }
}

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) {
            // 執(zhí)行啟動邏輯
            initializeServices();
            startBackgroundTasks();
            running = true;
            
            logger.info("CustomSmartLifecycle啟動完成");
        }
    }
    
    @Override
    public void stop() {
        logger.info("CustomSmartLifecycle開始停止");
        
        if (running) {
            // 執(zhí)行停止邏輯
            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("初始化后臺服務(wù)...");
        // 模擬服務(wù)初始化
    }
    
    private void startBackgroundTasks() {
        logger.info("啟動后臺任務(wù)...");
        
        // 啟動定時任務(wù)
        scheduledTask = taskScheduler.scheduleAtFixedRate(() -> {
            logger.debug("執(zhí)行后臺任務(wù)...");
            // 模擬后臺任務(wù)執(zhí)行
        }, Duration.ofSeconds(30));
    }
    
    private void stopBackgroundTasks() {
        logger.info("停止后臺任務(wù)...");
        
        if (scheduledTask != null && !scheduledTask.isCancelled()) {
            scheduledTask.cancel(true);
            logger.info("后臺任務(wù)已停止");
        }
    }
    
    private void cleanupResources() {
        logger.info("清理資源...");
        // 模擬資源清理
    }
}

12. 方法十:@Conditional 條件化初始化

12.1 基本使用

根據(jù)條件決定是否執(zhí)行初始化邏輯。

@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("創(chuàng)建CacheInitializer - 緩存功能已啟用");
        return new CacheInitializer();
    }
    
    @Bean
    @ConditionalOnClass(name = "com.example.ExternalService")
    public ExternalServiceInitializer externalServiceInitializer() {
        logger.info("創(chuàng)建ExternalServiceInitializer - 檢測到ExternalService類");
        return new ExternalServiceInitializer();
    }
    
    @Bean
    @ConditionalOnExpression("${app.mode:dev} == 'prod'")
    public ProductionInitializer productionInitializer() {
        logger.info("創(chuàng)建ProductionInitializer - 生產(chǎn)環(huán)境模式");
        return new ProductionInitializer();
    }
    
    @Bean
    @ConditionalOnWebApplication
    public WebApplicationInitializer webApplicationInitializer() {
        logger.info("創(chuàng)建WebApplicationInitializer - Web應(yīng)用環(huán)境");
        return new WebApplicationInitializer();
    }
    
    public static class CacheInitializer {
        @PostConstruct
        public void init() {
            logger.info("初始化緩存系統(tǒng)...");
            // 模擬緩存初始化
        }
    }
    
    public static class ExternalServiceInitializer {
        @PostConstruct
        public void init() {
            logger.info("初始化外部服務(wù)...");
            // 模擬外部服務(wù)初始化
        }
    }
    
    public static class ProductionInitializer {
        @PostConstruct
        public void init() {
            logger.info("執(zhí)行生產(chǎn)環(huán)境特定初始化...");
            // 生產(chǎn)環(huán)境特定的初始化邏輯
        }
    }
    
    public static class WebApplicationInitializer {
        @PostConstruct
        public void init() {
            logger.info("執(zhí)行Web應(yīng)用特定初始化...");
            // Web應(yīng)用特定的初始化邏輯
        }
    }
}

13. 執(zhí)行順序和優(yōu)先級

13.1 完整的執(zhí)行順序流程圖

graph TD
    A[Spring Boot應(yīng)用啟動] --> B[加載配置類靜態(tài)代碼塊]
    B --> C[創(chuàng)建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[應(yīng)用完全就緒]
    
    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 執(zhí)行順序驗證代碼

@Component
public class ExecutionOrderVerifier {
    
    // 1. 靜態(tài)代碼塊(類加載時執(zhí)行)
    static {
        System.out.println("1. 靜態(tài)代碼塊執(zhí)行");
    }
    
    public ExecutionOrderVerifier() {
        System.out.println("2. 構(gòu)造函數(shù)執(zhí)行");
    }
    
    @PostConstruct
    public void postConstruct() {
        System.out.println("3. @PostConstruct方法執(zhí)行");
    }
}

@Component
public class ExecutionOrderVerifier2 implements InitializingBean {
    
    @PostConstruct
    public void postConstruct() {
        System.out.println("4. @PostConstruct方法執(zhí)行(第二個Bean)");
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5. InitializingBean.afterPropertiesSet執(zhí)行");
    }
}

@Configuration
public class ExecutionOrderConfig {
    
    @Bean(initMethod = "initMethod")
    public OrderDemoBean orderDemoBean() {
        System.out.println("6. @Bean方法執(zhí)行 - 創(chuàng)建Bean實例");
        return new OrderDemoBean();
    }
    
    public static class OrderDemoBean {
        public void initMethod() {
            System.out.println("7. @Bean initMethod執(zhí)行");
        }
    }
}

@Component
@Order(1)
public class OrderApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("8. ApplicationRunner執(zhí)行 - Order(1)");
    }
}

@Component  
@Order(2)
public class OrderCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("9. CommandLineRunner執(zhí)行 - 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事件處理 - 應(yīng)用完全就緒");
    }
}

14. 實際應(yīng)用案例

14.1 完整的系統(tǒng)初始化器

@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("====== 系統(tǒng)初始化開始 ======");
        
        // 階段1: 基礎(chǔ)設(shè)施檢查
        checkInfrastructure();
        
        // 階段2: 數(shù)據(jù)初始化
        initializeData();
        
        // 階段3: 緩存預(yù)熱
        warmUpCaches();
        
        // 階段4: 服務(wù)啟動
        startServices();
        
        logger.info("====== 系統(tǒng)初始化完成 ======");
    }
    
    private void checkInfrastructure() {
        logger.info("階段1: 檢查基礎(chǔ)設(shè)施...");
        
        // 檢查數(shù)據(jù)庫
        checkDatabase();
        
        // 檢查Redis
        checkRedis();
        
        // 檢查磁盤空間
        checkDiskSpace();
        
        logger.info("基礎(chǔ)設(shè)施檢查完成");
    }
    
    private void checkDatabase() {
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            String dbName = metaData.getDatabaseProductName();
            String version = metaData.getDatabaseProductVersion();
            logger.info("數(shù)據(jù)庫連接正常: {} {}", dbName, version);
            
            // 檢查必要的表是否存在
            checkRequiredTables(connection);
            
        } catch (SQLException e) {
            logger.error("數(shù)據(jù)庫檢查失敗", e);
            throw new RuntimeException("數(shù)據(jù)庫不可用", 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響應(yīng)異常");
            }
        } 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: 初始化數(shù)據(jù)...");
        
        // 加載系統(tǒng)配置
        loadSystemConfigs();
        
        // 初始化基礎(chǔ)數(shù)據(jù)
        initializeBaseData();
        
        logger.info("數(shù)據(jù)初始化完成");
    }
    
    private void loadSystemConfigs() {
        try {
            List<SystemConfig> configs = configRepository.findAll();
            logger.info("加載了 {} 條系統(tǒng)配置", configs.size());
            
            // 將配置加載到內(nèi)存或緩存中
            configs.forEach(config -> {
                logger.debug("系統(tǒng)配置: {} = {}", config.getKey(), config.getValue());
            });
            
        } catch (Exception e) {
            logger.error("系統(tǒng)配置加載失敗", e);
            throw new RuntimeException("系統(tǒng)配置加載失敗", e);
        }
    }
    
    private void initializeBaseData() {
        logger.info("初始化基礎(chǔ)數(shù)據(jù)...");
        // 模擬基礎(chǔ)數(shù)據(jù)初始化
        // 如:默認用戶、角色、權(quán)限等
    }
    
    private void warmUpCaches() {
        logger.info("階段3: 緩存預(yù)熱...");
        
        // 預(yù)熱用戶緩存
        warmUpUserCache();
        
        // 預(yù)熱配置緩存
        warmUpConfigCache();
        
        logger.info("緩存預(yù)熱完成");
    }
    
    private void warmUpUserCache() {
        logger.info("預(yù)熱用戶緩存...");
        // 模擬用戶緩存預(yù)熱
    }
    
    private void warmUpConfigCache() {
        logger.info("預(yù)熱配置緩存...");
        // 模擬配置緩存預(yù)熱
    }
    
    private void startServices() {
        logger.info("階段4: 啟動服務(wù)...");
        
        // 啟動定時任務(wù)服務(wù)
        startScheduledTasks();
        
        // 啟動消息監(jiān)聽服務(wù)
        startMessageListeners();
        
        logger.info("服務(wù)啟動完成");
    }
    
    private void startScheduledTasks() {
        logger.info("啟動定時任務(wù)...");
        // 模擬定時任務(wù)啟動
    }
    
    private void startMessageListeners() {
        logger.info("啟動消息監(jiān)聽器...");
        // 模擬消息監(jiān)聽器啟動
    }
}

15. 最佳實踐和注意事項

15.1 最佳實踐

合理選擇初始化方式

  • Bean初始化使用 @PostConstruct
  • 應(yīng)用啟動后任務(wù)使用 ApplicationRunner
  • 需要命令行參數(shù)的使用 CommandLineRunner

控制初始化順序

  • 使用 @Order 注解控制多個 Runner 的執(zhí)行順序
  • 理解不同初始化方式的執(zhí)行時機

異常處理

  • 初始化失敗應(yīng)該快速失敗
  • 提供清晰的錯誤信息
  • 區(qū)分關(guān)鍵初始化和非關(guān)鍵初始化

性能考慮

  • 避免在初始化階段執(zhí)行耗時操作
  • 長時間任務(wù)應(yīng)該異步執(zhí)行
  • 考慮使用懶加載

15.2 注意事項

  1. 避免循環(huán)依賴:在初始化階段特別注意Bean之間的依賴關(guān)系
  2. 配置驗證:盡早驗證配置的正確性
  3. 資源清理:對于需要清理的資源,實現(xiàn)相應(yīng)的銷毀方法
  4. 日志記錄:詳細記錄初始化過程,便于問題排查

16. 總結(jié)

本文詳細介紹了 Spring Boot 中 10 種在啟動時執(zhí)行特定代碼的方式,涵蓋了從簡單的注解到復(fù)雜的事件監(jiān)聽等各種場景。每種方式都有其特定的使用場景和執(zhí)行時機,在實際項目中應(yīng)該根據(jù)具體需求選擇合適的方式。

通過合理使用這些初始化機制,可以確保應(yīng)用在啟動時完成所有必要的準備工作,為應(yīng)用的穩(wěn)定運行奠定堅實的基礎(chǔ)。

以上就是SpringBoot啟動時執(zhí)行特定代碼的10種方式的詳細內(nèi)容,更多關(guān)于SpringBoot啟動時執(zhí)行特定代碼的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論