在托管bean中,@PostConstruct
在常规Java对象构造函数之后调用。
为什么我会使用@PostConstruct
来初始化bean而不是常规构造函数本身?
答案 0 :(得分:357)
因为在调用构造函数时,bean尚未初始化 - 即没有注入依赖项。在@PostConstruct
方法中,bean已完全初始化,您可以使用依赖项。
因为这是保证在bean生命周期中只调用一次此方法的契约。可能会发生(尽管不太可能)容器在其内部工作中多次实例化bean,但它保证@PostConstruct
只会被调用一次。
答案 1 :(得分:72)
主问题是:
在构造函数中,依赖项的注入尚未发生*
*显然不包括构造函数注入
真实世界的例子:
public class Foo {
@Inject
Logger LOG;
@PostConstruct
public void fooInit(){
LOG.info("This will be printed; LOG has already been injected");
}
public Foo() {
LOG.info("This will NOT be printed, LOG is still null");
// NullPointerException will be thrown here
}
}
重要强>:
@PostConstruct
和@PreDestroy
已完全removed in Java 11 。
要继续使用它们,您需要将javax.annotation-api JAR添加到依赖项中。
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
答案 2 :(得分:52)
如果你的类在构造函数中执行所有初始化,那么@PostConstruct
确实是多余的。
但是,如果您的类使用setter方法注入其依赖项,则该类的构造函数无法完全初始化该对象,并且有时需要在调用所有setter方法后执行某些初始化,因此{{{}的用例1}}。
答案 3 :(得分:5)
考虑以下情况:
public class Car {
@Inject
private Engine engine;
public Car() {
engine.initialize();
}
...
}
由于Car必须在字段注入之前进行实例化,因此注入点引擎在构造函数执行期间仍为null,从而导致NullPointerException。
这个问题可以通过{@ 3}}构造函数注入或Java @PostConstruct方法注释的JSR 250 Common Annotations来解决。
<强> @PostConstruct 强>
JSR-250定义了一组通用的注释,这些注释已包含在Java SE 6中。
PostConstruct注释用于需要的方法 在完成依赖注入以执行任何操作之后执行 初始化。必须在放入类之前调用此方法 投入使用。所有类都必须支持此注释 支持依赖注入。
JSR-250 Chap。 2.5 javax.annotation.PostConstruct
@PostConstruct注释允许在实例化实例并执行所有注入后定义要执行的方法。
public class Car {
@Inject
private Engine engine;
@PostConstruct
public void postConstruct() {
engine.initialize();
}
...
}
不是在构造函数中执行初始化,而是将代码移动到使用@PostConstruct注释的方法。
后构造方法的处理很简单,只需查找使用@PostConstruct注释的所有方法并依次调用它们。
private void processPostConstruct(Class type, T targetInstance) {
Method[] declaredMethods = type.getDeclaredMethods();
Arrays.stream(declaredMethods)
.filter(method -> method.getAnnotation(PostConstruct.class) != null)
.forEach(postConstructMethod -> {
try {
postConstructMethod.setAccessible(true);
postConstructMethod.invoke(targetInstance, new Object[]{});
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
});
}
必须在实例化和注射完成后执行构造后方法的处理。
答案 4 :(得分:1)
当涉及某种代理或远程处理时,基于构造函数的初始化也不会按预期工作。
每当EJB反序列化时,以及每当为其创建新代理时,都会调用ct ...