web-dev-qa-db-ja.com

@PostConstructを使用する理由

マネージドBeanでは、@PostConstructは通常のJavaオブジェクト・コンストラクターの後に呼び出されます。

通常のコンストラクタ自体ではなく、なぜ@PostConstructを使ってbeanで初期化するのですか?

255
Jan
  • なぜなら、コンストラクタが呼び出されたときには、Beanはまだ初期化されていないからです。つまり、依存関係は注入されません。 @PostConstructメソッドでは、Beanは完全に初期化されており、依存関係を使用できます。

  • これは、このメソッドがBeanのライフサイクル内で一度だけ呼び出されることを保証する規約であるためです。 Beanがその内部作業でコンテナによって複数回インスタンス化されることは起こりそうにありませんが、@PostConstructが一度だけ呼び出されることを保証します。

361
Bozho

主な問題は、次のとおりです。

コンストラクタでは、依存関係の注入はまだ行われていません*

*明らかにコンストラクタインジェクションを除く


実世界の例:

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

IMPORTANT@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>

Gradle

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

クラスがコンストラクターですべての初期化を実行する場合、@PostConstructは確かに冗長です。

ただし、クラスのセッターメソッドを使用して依存関係が注入されている場合、クラスのコンストラクターはオブジェクトを完全に初期化できず、すべてのセッターメソッドが呼び出された後に初期化を実行する必要がある場合があるため、@PostConstructのユースケース。

52
skaffman

次のシナリオを検討してください。

public class Car {
  @Inject
  private Engine engine;  

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

Carはフィールドインジェクションの前にインスタンス化する必要があるため、コンストラクターの実行中はインジェクションポイントエンジンはまだnullのままであり、結果としてNullPointerExceptionが発生します。

この問題は、 JSR-330 Dependency Injection for Java コンストラクタ注入、またはJSR 250 Common Annotations for Java @PostConstructメソッドアノテーションのいずれかによって解決できます。

@ PostConstruct

JSR-250はJava SE 6に含まれているアノテーションの共通セットを定義しています。

PostConstructアノテーションは、初期化を実行するために依存性注入が行われた後に実行する必要があるメソッドで使用されます。このメソッドはクラスがインサービスになる前に呼び出さなければなりません。このアノテーションは依存性注入をサポートするすべてのクラスでサポートされなければなりません。

JSR-250チャップ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);
        }
      });
}

構築後メソッドの処理は、インスタンス化と注入が完了した後に実行する必要があります。

5
Humoyun

また、ある種のプロキシやリモート処理が含まれていると、コンストラクタベースの初期化が意図したとおりに機能しません。

EJBが逆シリアル化されるたびに、そしてそれに対して新しいプロキシが作成されるたびに、ctが呼び出されます。

1
struberg