为什么要使用@PostConstruct?

时间:2010-08-04 14:32:47

标签: java dependency-injection java-ee cdi jsr299

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

为什么我会使用@PostConstruct来初始化bean而不是常规构造函数本身?

5 个答案:

答案 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添加到依赖项中。

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

摇篮

// 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 ...