@PostConstruct注解详细分析,结合案例和使用场景(保姆级教程)
@PostConstruct注解详细分析,结合案例和使用场景(保姆级教程)
@PostConstruct注解是Java中用于生命周期管理的注解,主要用于执行一些初始化逻辑。本文将详细介绍@PostConstruct注解的使用场景和具体案例,帮助读者更好地理解和应用这一注解。
@PostConstruct 注解
从 Java 9 开始,javax.annotation.PostConstruct 等注解被标记为过时(deprecated),并在后续的 Java 版本中可能会被移除。因此,如果你在使用 Java 9 或更高版本,并且你的项目不是 Java EE 项目,你可能需要考虑使用其他方式来代替 @PostConstruct 注解,比如直接在构造函数中执行初始化操作,或者使用 Spring 的@Bean 注解配合 initMethod 属性来指定初始化方法。
然而,在Spring框架中,@PostConstruct 注解仍然是被支持和推荐的。
一、什么是 @PostConstruct 注解
- @PostConstruct 是 java 中用于 生命周期管理的注解,是 java 自己的 的注解,不是 spring 的。
- 当应用程序加载时,@PostConstruct 标注的方法会在 类实例化 并 完成依赖注入 后 自动调用。
- 作用:主要作用是执行一些 初始化逻辑,例如预加载资源、启动后台任务、校验依赖注入等。
- 执行时机:首先创建对象(实例化Bean),依赖注入完成(属性赋值),@PostConstruct 标记的方法被自动调用。
- 注意:
- 只能标记在 非静态的 void() 方法 。
- 每个类中最多只能有一个
@PostConstruct
注解。 - 如果标注的方法抛出异常,容器会报告错误,可能终止应用程序的启动。
- 标记的方法只执行一次。
- 如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用
@PostConstruct
注解来完成。
二、使用案例
1. 数据预热
使用 Redis 进行的数据预热,需要项目启动以后,触发第一次调用才能生成缓存,而利用 @PostConstruct
注解能让预热数据在 Bean 初始化阶段完成,比 Redis 更早。
public interface DataWarmingService {
String data();
}
@Service
public class DataWarmingServiceImpl implements DataWarmingService {
@Override
public String data() {
System.out.println("执行生成数据的方法");
return "Data Warming";
}
}
@Configuration
public class DataWarmingConfig {
@Autowired
private DataWarmingService dataWarmingService;
//定义属性:模拟需要预热的数据
private static String dataWarm;
@PostConstruct
public void init(){
System.out.println("Autowired 加载完成!");
dataWarm = dataWarmingService.data();
System.out.println(dataWarm);
}
}
启动启动类,运行结果:
分析:
创建实例:
DataWarmingServiceImpl 是实现类,被标注为 @Service,Spring 会将其注册为 Bean。DataWarmingConfig 加了 @Configuration 注解,然后也会创建实例。依赖注入:
Spring 发现 DataWarmingConfig 中的 @Autowired 注解,自动将 DataWarmingServiceImpl 注入到 dataWarmingService 属性中。调用 @PostConstruct 方法:Spring 在依赖注入完成后,检查 DataWarmingConfig 类中是否存在 @PostConstruct 标注的方法。
2. 加载配置文件
使用 @PostConstruct 将 @Value 注入的实例变量值 赋给 静态变量,可以解决 static 无法直接与 @Value 注解联动的问题。
具体分析:
@Value 注解的特性:@Value 注解修饰的常量不能是静态的,否则会 null,因为 static 的加载在 @Value 之前。因此它只能注入到 实例变量 或 实例方法 中,而不能直接注入到静态变量。
@Component
public class ConstantUtil {
// 使用 @Value 注解从配置文件中注入值
@Value("${app.name:defaultAppName}") // 如果配置文件中没有 app.name,默认值为 "defaultAppName"
private String appName;
// 定义一个静态变量用于全局访问
private static String STATIC_APP_NAME;
@PostConstruct
public void init() {
STATIC_APP_NAME = this.appName;
}
}
@Test
public void test() {
System.out.println(STATIC_APP_NAME);
}
//输出:defaultAppName(假设这里没有配置文件)
分析:
- 类加载阶段:
- 类 ConstantUtil 被加载,静态变量 STATIC_APP_NAME 被初始化为 null。
- @Value 注解暂时未生效,因为 Bean 尚未被实例化。
- Bean 实例化、依赖注入阶段:
- Spring 扫描到 @Component 标注的类,将其实例化为 Bean。
- @Value 注入配置文件中的 app.name 值到实例变量 appName。
- @PostConstruct 方法执行:
- Spring 在完成依赖注入后调用 init() 方法。
- 方法内,将实例变量 appName 的值赋给静态变量 STATIC_APP_NAME。
再看个例子:
@Component
public class LoadConfig {
@Value("${server.port:abc}")
private String port;
// 模拟静态常量
public static String server_port;
@PostConstruct
public void construct() {
System.out.println("Before PostConstruct:" + server_port);
server_port = port;
System.out.println("After PostConstruct:" + server_port);
}
}