@PostConstructを使用する理由


294

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

@PostConstruct通常のコンストラクタ自体ではなく、なぜBeanによる初期化に使用するのですか?


4
依存関係を許可するために、コンストラクター注入が一般的に好まれているという印象を受けましたfinal。そのパターンを考えると、なぜ@PostConstructJ2EEに追加されているのでしょうか。彼らは別のユースケースを確実に見たはずです。
mjaggard 2018年

回答:


409
  • コンストラクタが呼び出されたとき、Beanはまだ初期化されていないため、依存関係は注入されません。では@PostConstructこの方法Beanが完全に初期化され、あなたは依存関係を使用することができます。

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


17
コンストラクター自体がすべての依存関係を自動配線する場合-次に、Beanをコンストラクターで完全に初期化することもできます(すべての自動配線されたフィールドを手動で設定した後)。
yair 2013年

7
Beanのコンストラクターが複数回呼び出される可能性があるのはどのような場合ですか?
yair 2013年

1
おそらく「パッシベーション」のようなものです。コンテナーがBeanをディスクストアに保存し、そこから復元することを決定した場合。
Bozho

13
コンストラクタが複数回呼び出される可能性はそれほど高くありません。コンテナがプロキシをインスタンス化すると、プロキシに対して少なくとも1回、実際のBeanに対して1回、コンストラクタが呼び出されることがわかります。
marcus 14年

サーバーが再起動した直後にページが読み込まれる場合、@ 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およびJava 11では@PreDestroy 完全に削除されました

それらを使い続けるには、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'

19
in a constructor, the injection of the dependencies has not yet occurred. セッターまたはフィールドインジェクションでは真ですが、コンストラクタインジェクションでは真ではありません。
Adam Siemion、2015年

@PostConstructがJava 11で削除されたので、Java 11でこの実世界の例をどのように処理できるでしょうか?
2018年

@tet回答で述べたように、javax.annotation-apiライブラリを使用する必要があります。これらのアノテーションは、Java 11で削除されたが、すでにJavaの9から非推奨マークされていた
ナレンドラ-Choudharyさん

63

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

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


@staffman:私の側からの1つ。データベースからフェッチした値でinputtextフィールドを初期化したい場合、PostConstructを使用してそれを行うことができますが、コンストラクター内で同じことをしようとすると失敗します。PostContructを使用せずに初期化するこの要件があります。あなたは時間があれば、あなたも、このいずれかを答えてくださいすることができますstackoverflow.com/questions/27540573/...
Shirgill Farhan

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

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


1

また、コンストラクタベースの初期化は、何らかのプロキシまたはリモート処理が関係する場合は、意図したとおりに機能しません。

ctは、EJBがデシリアライズされるたび、および新しいプロキシが作成されるたびに呼び出されます...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.