Skip to content

生命周期

理解 LCGYL Framework 的生命周期管理,帮助你更好地控制应用和组件的行为。

应用生命周期

生命周期阶段

LCGYL 应用经历以下生命周期阶段:

启动 → 初始化 → 运行 → 停止 → 销毁

启动应用

java
public class Application {
    
    public static void main(String[] args) {
        // 启动应用
        LcgylApplication app = LcgylApplication.run(Application.class, args);
        
        // 应用现在处于运行状态
        System.out.println("应用已启动");
    }
}

停止应用

java
// 优雅停止
app.stop();

// 或使用 shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    app.stop();
}));

组件生命周期

生命周期回调

组件可以定义生命周期回调方法,在特定阶段执行逻辑。

@PostConstruct - 初始化

组件创建后立即调用,用于初始化资源。

java
@Component
public class DatabaseService {
    
    private Connection connection;
    
    @PostConstruct
    public void init() {
        System.out.println("初始化数据库连接...");
        connection = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/mydb",
            "user",
            "password"
        );
        System.out.println("数据库连接已建立");
    }
    
    public void query(String sql) {
        // 使用连接执行查询
    }
}

@PreDestroy - 清理

组件销毁前调用,用于释放资源。

java
@Component
public class DatabaseService {
    
    private Connection connection;
    
    @PreDestroy
    public void cleanup() {
        System.out.println("关闭数据库连接...");
        if (connection != null) {
            try {
                connection.close();
                System.out.println("数据库连接已关闭");
            } catch (SQLException e) {
                System.err.println("关闭连接失败: " + e.getMessage());
            }
        }
    }
}

完整示例

java
@Component
public class CacheService {
    
    private static final Logger logger = LoggerFactory.getLogger(CacheService.class);
    
    private Cache<String, Object> cache;
    private ScheduledExecutorService scheduler;
    
    @PostConstruct
    public void init() {
        logger.info("初始化缓存服务...");
        
        // 创建缓存
        cache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();
        
        // 启动定时清理任务
        scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(
            () -> cache.cleanUp(),
            1, 1, TimeUnit.HOURS
        );
        
        logger.info("缓存服务初始化完成");
    }
    
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    
    public Object get(String key) {
        return cache.getIfPresent(key);
    }
    
    @PreDestroy
    public void cleanup() {
        logger.info("清理缓存服务...");
        
        // 停止定时任务
        if (scheduler != null) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
            }
        }
        
        // 清空缓存
        if (cache != null) {
            cache.invalidateAll();
        }
        
        logger.info("缓存服务已清理");
    }
}

生命周期顺序

单个组件

1. 构造器
2. 依赖注入
3. @PostConstruct
4. 组件可用
5. @PreDestroy
6. 组件销毁

多个组件

组件按依赖关系顺序初始化和销毁。

java
@Component
public class ServiceA {
    @PostConstruct
    public void init() {
        System.out.println("ServiceA 初始化");
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("ServiceA 清理");
    }
}

@Component
public class ServiceB {
    
    @Inject
    private ServiceA serviceA;  // 依赖 ServiceA
    
    @PostConstruct
    public void init() {
        System.out.println("ServiceB 初始化");
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("ServiceB 清理");
    }
}

输出

ServiceA 初始化
ServiceB 初始化
... 应用运行 ...
ServiceB 清理
ServiceA 清理

启动和停止钩子

应用启动钩子

在应用启动完成后执行。

java
@Component
public class StartupListener {
    
    @OnApplicationStart
    public void onStart() {
        System.out.println("应用启动完成!");
        // 执行启动后的任务
        // - 预热缓存
        // - 加载配置
        // - 注册服务
    }
}

应用停止钩子

在应用停止前执行。

java
@Component
public class ShutdownListener {
    
    @OnApplicationStop
    public void onStop() {
        System.out.println("应用即将停止...");
        // 执行清理任务
        // - 保存状态
        // - 关闭连接
        // - 释放资源
    }
}

异步初始化

对于耗时的初始化操作,可以使用异步方式。

java
@Component
public class DataPreloadService {
    
    private static final Logger logger = LoggerFactory.getLogger(DataPreloadService.class);
    
    @Inject
    private DataRepository repository;
    
    @PostConstruct
    public void init() {
        // 异步预加载数据
        CompletableFuture.runAsync(() -> {
            logger.info("开始预加载数据...");
            
            try {
                List<Data> data = repository.loadAll();
                // 处理数据
                logger.info("数据预加载完成,共 {} 条", data.size());
            } catch (Exception e) {
                logger.error("数据预加载失败", e);
            }
        });
        
        logger.info("数据预加载任务已启动");
    }
}

延迟初始化

使用 @Lazy 延迟组件初始化。

java
@Component
@Lazy
public class HeavyService {
    
    @PostConstruct
    public void init() {
        System.out.println("HeavyService 初始化(延迟)");
        // 耗时的初始化操作
    }
}

@Component
public class UserService {
    
    @Inject
    @Lazy
    private HeavyService heavyService;  // 只在首次使用时初始化
    
    public void doSomething() {
        // 首次访问时才初始化 HeavyService
        heavyService.process();
    }
}

条件初始化

根据条件决定是否初始化组件。

java
@Component
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
public class FeatureService {
    
    @PostConstruct
    public void init() {
        System.out.println("功能已启用,初始化服务");
    }
}

优雅停机

确保应用能够优雅地停止。

java
@Component
public class GracefulShutdown {
    
    private static final Logger logger = LoggerFactory.getLogger(GracefulShutdown.class);
    
    @Inject
    private TaskExecutor taskExecutor;
    
    @PreDestroy
    public void shutdown() {
        logger.info("开始优雅停机...");
        
        // 1. 停止接收新任务
        taskExecutor.shutdown();
        
        // 2. 等待现有任务完成
        try {
            if (!taskExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                logger.warn("等待超时,强制停止");
                taskExecutor.shutdownNow();
                
                // 再等待一段时间
                if (!taskExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
                    logger.error("无法停止所有任务");
                }
            }
        } catch (InterruptedException e) {
            logger.error("停机过程被中断", e);
            taskExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        
        logger.info("优雅停机完成");
    }
}

健康检查

实现健康检查接口,监控组件状态。

java
@Component
public class DatabaseHealthCheck implements HealthCheck {
    
    @Inject
    private DatabaseService databaseService;
    
    @Override
    public HealthStatus check() {
        try {
            // 检查数据库连接
            databaseService.ping();
            return HealthStatus.UP;
        } catch (Exception e) {
            return HealthStatus.DOWN;
        }
    }
}

最佳实践

1. 保持初始化快速

java
// ✅ 推荐:快速初始化
@PostConstruct
public void init() {
    // 只做必要的初始化
    connection = createConnection();
}

// ❌ 不推荐:耗时初始化
@PostConstruct
public void init() {
    // 预加载大量数据
    loadMillionsOfRecords();  // 阻塞启动
}

2. 处理初始化异常

java
@PostConstruct
public void init() {
    try {
        connection = createConnection();
    } catch (Exception e) {
        logger.error("初始化失败", e);
        // 决定是否抛出异常
        throw new InitializationException("无法建立数据库连接", e);
    }
}

3. 确保资源释放

java
@PreDestroy
public void cleanup() {
    // 使用 try-catch 确保所有资源都被释放
    try {
        closeConnection();
    } catch (Exception e) {
        logger.error("关闭连接失败", e);
    }
    
    try {
        releaseCache();
    } catch (Exception e) {
        logger.error("释放缓存失败", e);
    }
}

4. 避免循环依赖

java
// ❌ 不推荐:在 @PostConstruct 中访问依赖的组件
@Component
public class ServiceA {
    @Inject
    private ServiceB serviceB;
    
    @PostConstruct
    public void init() {
        serviceB.doSomething();  // 可能导致问题
    }
}

// ✅ 推荐:使用事件或延迟初始化
@Component
public class ServiceA {
    @Inject
    private ServiceB serviceB;
    
    @OnApplicationStart
    public void onStart() {
        serviceB.doSomething();  // 安全
    }
}

5. 记录生命周期事件

java
@Component
public class MyService {
    
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);
    
    @PostConstruct
    public void init() {
        logger.info("MyService 初始化开始");
        // 初始化逻辑
        logger.info("MyService 初始化完成");
    }
    
    @PreDestroy
    public void cleanup() {
        logger.info("MyService 清理开始");
        // 清理逻辑
        logger.info("MyService 清理完成");
    }
}

常见问题

Q: @PostConstruct 和构造器有什么区别?

A:

  • 构造器:对象创建时调用,此时依赖还未注入
  • @PostConstruct:依赖注入完成后调用,可以安全使用依赖
java
@Component
public class MyService {
    
    @Inject
    private UserRepository repository;
    
    public MyService() {
        // ❌ repository 还是 null
        // repository.findAll();
    }
    
    @PostConstruct
    public void init() {
        // ✅ repository 已注入
        repository.findAll();
    }
}

Q: 如何控制初始化顺序?

A: 使用 @DependsOn 注解。

java
@Component
@DependsOn("databaseService")
public class CacheService {
    // 确保 DatabaseService 先初始化
}

Q: 可以有多个 @PostConstruct 方法吗?

A: 可以,但不推荐。如果有多个,执行顺序不确定。

java
// ❌ 不推荐
@Component
public class MyService {
    @PostConstruct
    public void init1() { }
    
    @PostConstruct
    public void init2() { }
}

// ✅ 推荐
@Component
public class MyService {
    @PostConstruct
    public void init() {
        init1();
        init2();
    }
    
    private void init1() { }
    private void init2() { }
}

下一步

Released under the Apache License 2.0