为什么要使用@PostConstruct?


294

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

为什么要使用@PostConstructbean初始化而不是常规构造函数本身?


4
我的印象是,通常更喜欢使用构造函数注入来允许依赖final。在这种模式下,为什么@PostConstruct要添加到J2EE中-他们一定一定已经看到了另一个用例?
mjaggard

Answers:


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

  • 因为这是保证在bean生命周期中仅调用一次此方法的协定。可能会发生(尽管不太可能)容器在其内部工作中多次实例化bean,但它保证@PostConstruct仅将其调用一次。


17
如果构造函数本身自动装配所有依赖项,那么也可以在构造函数中完全初始化Bean(在手动设置所有自动装配的字段之后)。
yair

7
在什么情况下可能多次调用bean的构造函数?
yair

1
大概像“钝化”。如果容器决定将Bean存储在磁盘存储上,然后从那里还原它。
2013年

13
构造函数被多次调用不是不太可能。当容器实例化一个代理时,您将看到该代理的构造函数至少被调用一次,而真正的bean被调用一次。
marcus

请注意,在服务器重新启动后立即加载页面时,不会调用@PostConstruct方法。(这可能是JBoss的错误。)
Dave Jarvis,

96

主要问题是:

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

*显然不包括构造函数注入


实际示例:

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中完全删除

为了继续使用它们,您需要将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'

19
in a constructor, the injection of the dependencies has not yet occurred. 对于setter或field注入为true,但对于构​​造函数注入为true。
亚当·西缅

在Java 11中删除@PostConstruct之后,如何使用Java 11处理这个真实示例?
tet

@tet如答案中所述,您需要使用javax.annotation-api库。这些注解在Java中11被拆除,但已经标志着自Java的9弃用
纳伦德拉- CHOUDHARY

63

如果您的类在构造函数中执行了所有初始化操作,则@PostConstruct确实是多余的。

但是,如果您的类使用setter方法注入了其依赖项,则该类的构造函数无法完全初始化该对象,有时,在调用了所有setter方法之后,需要执行一些初始化,因此是的用例@PostConstruct


@工作人员:在我这边再加一个。如果我希望使用从数据库中获取的值来初始化输入文本字段,则可以在PostConstruct的帮助下完成此操作,但是在构造函数中尝试执行相同操作时将失败。我有这项要求,无需使用PostContruct即可初始化。如果你有时间,可以请你也回答这一个stackoverflow.com/questions/27540573/...
Shirgill尔汉

10

请考虑以下情形:

public class Car {
  @Inject
  private Engine engine;  

  public Car() {
    engine.initialize();  
  }
  ...
}

由于必须在字段注入之前实例化Car,因此在构造函数执行期间注入点引擎仍为null,从而导致NullPointerException。

此问题可以通过Java构造函数注入的JSR-330依赖项注入或Java @PostConstruct方法注释的JSR 250通用注释来解决。

@PostConstruct

JSR-250定义了一组通用注释,这些注释已包含在Java SE 6中。

PostConstruct批注用于需要依赖注入完成以执行任何初始化之后需要执行的方法上。必须在类投入使用之前调用此方法。所有支持依赖注入的类都必须支持该注释。

JSR-250第1章。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);
        }
      });
}

构造和注入完成后,必须执行构造后方法的处理。


1

同样,只要涉及某种代理或远程处理,基于构造函数的初始化将无法按预期工作。

每当对EJB进行反序列化以及为它创建新的代理时,都会调用ct。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.