生命周期
理解 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() { }
}