为什么要使用@PostConstruct?

Jan*_*Jan 274 java dependency-injection jsr299 java-ee cdi

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

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

Boz*_*zho 382

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

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

  • 如果构造函数本身自动装配所有依赖项 - 那么bean也可以在构造函数中完全初始化(在手动设置所有自动装配的字段之后). (12认同)
  • 并不是不太可能看到多次调用构造函数.当容器实例化代理时,您将看到构造函数至少为代理调用一次,对真实bean调用一次. (11认同)
  • 是什么情况下bean的构造函数可能不止一次被调用? (6认同)
  • 可能类似于“钝化”。如果容器决定将 bean 存储在磁盘存储上,然后从那里恢复它。 (2认同)

And*_*ios 78

主要问题是:

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

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


现实世界的例子:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

重要提示: @PostConstruct@PreDestroy 已经完全在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>
Run Code Online (Sandbox Code Playgroud)

摇篮

// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
Run Code Online (Sandbox Code Playgroud)

  • `在构造函数中,尚未发生依赖项的注入.`使用setter或field注入时为true,但构造函数注入时则不然. (13认同)

ska*_*man 58

如果您的类在构造函数中执行所有初始化,那么@PostConstruct确实是多余的.

但是,如果你的类使用setter方法注入了它的依赖项,那么类的构造函数不能完全初始化对象,有时需要在调用所有setter方法之后执行一些初始化,因此用例是@PostConstruct.


Hum*_*mad 9

请考虑以下情形:

public class Car {
  @Inject
  private Engine engine;  

  public Car() {
    engine.initialize();  
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

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

这个问题可以通过Java构造函数注入的JSR-330依赖注入或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();  
  }
  ...
} 
Run Code Online (Sandbox Code Playgroud)

代码不是在构造函数中执行初始化,而是移动到使用@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);
        }
      });
}
Run Code Online (Sandbox Code Playgroud)

必须在实例化和注入完成后执行后构造方法的处理.