为什么要使用@PostConstruct?

418

在托管的Bean中,@PostConstruct 是在常规Java对象构造函数之后调用的。

为什么我要使用@PostConstruct来初始化我的bean,而不是直接使用常规构造函数呢?


7
我得到的印象是,通常更喜欢使用构造函数注入来允许依赖项为final。鉴于这种模式,为什么J2EE要添加@PostConstruct呢?他们一定又看到了其他用例吧? - mjaggard
@mjaggard 我的理解是 @PostConstruct 并不用于适当地注入依赖项,以确保它们是 final 等;它被用作一个实用程序的注释,该实用程序应该被 IoC 容器构造多次调用 一次。虽然我不知道这在容器中如何发生,但显然它是可能发生的(请参见已接受的答案)。 - Jason
4个回答

566
  • 因为在调用构造函数时,bean 尚未初始化,即没有注入任何依赖项。在 @PostConstruct 方法中,bean 已经完全初始化,您可以使用依赖项。

  • 因为这是保证该方法仅在 bean 生命周期中被调用一次的协议。虽然容器在内部工作中可能多次实例化一个 bean,但它保证只会调用一次 @PostConstruct


31
如果构造函数本身自动装配所有依赖项,则可以在构造函数中完全初始化bean(在手动设置所有自动装配字段之后)。 - yair
9
在什么情况下,一个 bean 的构造函数可能会被调用多次? - yair
2
可能是类似于"钝化"的东西。如果容器决定将Bean存储在磁盘存储器上,然后从那里恢复它。 - Bozho
14
看到构造函数被多次调用并不是那么不可能。当容器实例化代理时,您将看到构造函数至少为代理和真实Bean各调用一次。 - marcus
@PostConstruct被调用了两次,你遇到过这样的问题吗? - iamrajshah
显示剩余9条评论

146
你应该始终优先使用构造函数注入,但是如果由于某种原因你必须使用字段注入,在这种情况下,主要问题是:

在构造函数中,依赖项的注入尚未发生


例子

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在Java 11中已完全移除(https://jaxenter.com/jdk-11-java-ee-modules-140674.html)。

如果要继续使用它们,您需要将javax.annotation-api JAR添加到您的依赖项中。

Maven

<!-- 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>

Gradle
// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'

29
在构造函数中,尚未注入依赖项。 这对于使用setter或field注入是正确的,但使用构造函数注入则不是正确的。 - Adam Siemion
随着Java 11中@PostConstruct的移除,我们现在该如何处理这个真实世界的例子呢? - tet
正如答案中提到的,您需要使用javax.annotation-api库。这些注释已在Java 11中删除,但自Java 9以来已被标记为弃用。 - narendra-choudhary
我仍然可以在Java 11中看到@PostConstruct - Rookie007
@Rookie007 如果你检查一下你的类路径,你会发现你正在使用 javax.annotation-api jar。 - soung

78

如果你的类在构造函数中完成了所有初始化操作,那么@PostConstruct确实是多余的。

但是,如果你的类使用setter方法注入其依赖关系,那么类的构造函数就不能完全初始化对象,有时需要在调用所有setter方法后执行某些初始化操作,因此需要使用@PostConstruct


@staffman:我也支持这个问题。如果我想要用从数据库获取的值初始化一个inputtext字段,我可以在PostConstruct的帮助下做到,但是在构造函数内部尝试相同的操作时会失败。我有这个要求,需要在不使用PostContruct的情况下进行初始化。如果您有时间,请回答这个问题:http://stackoverflow.com/questions/27540573/how-to-initialize-inputtextfield-with-a-value-from-database-on-runtime-without-t - Farhan stands with Palestine

1

当涉及到代理或远程调用时,基于构造函数的初始化也无法按预期工作。

每当EJB被反序列化时,都会调用ct,并且每当为其创建新代理时也会调用ct...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接