Mengapa menggunakan @PostConstruct?

294

Dalam kacang yang dikelola, @PostConstructdipanggil setelah konstruktor objek Java biasa.

Mengapa saya gunakan @PostConstructuntuk menginisialisasi dengan kacang, bukan konstruktor biasa itu sendiri?

Jan
sumber
4
Saya mendapat kesan bahwa injeksi konstruktor umumnya lebih disukai untuk memungkinkan ketergantungan final. Mengingat pola itu, mengapa @PostConstructditambahkan ke J2EE - mereka pasti melihat use case lain pasti?
mjaggard

Jawaban:

409
  • karena ketika konstruktor dipanggil, kacang belum diinisialisasi - yaitu tidak ada ketergantungan yang disuntikkan. Dalam @PostConstructmetode kacang sepenuhnya diinisialisasi dan Anda dapat menggunakan dependensi.

  • karena ini adalah kontrak yang menjamin bahwa metode ini akan dipanggil hanya sekali dalam siklus hidup kacang. Mungkin terjadi (walaupun tidak mungkin) bahwa sebuah kacang dipakai berkali-kali oleh wadah dalam pekerjaan internalnya, tetapi itu menjamin bahwa @PostConstructakan dipanggil hanya sekali.

Bozho
sumber
17
dalam kasus konstruktor itu sendiri autowires semua dependensi - maka kacang juga dapat sepenuhnya diinisialisasi dalam konstruktor (setelah pengaturan secara manual semua bidang autowired).
yair
7
apa kasus di mana konstruktor kacang dapat dipanggil lebih dari sekali?
yair
1
Mungkin sesuatu seperti "pasivasi". Jika wadah memutuskan untuk menyimpan kacang di toko disk dan kemudian mengembalikannya dari sana.
Bozho
13
Bukannya tidak mungkin melihat konstruktor dipanggil beberapa kali. Ketika wadah instantiate proxy, Anda akan melihat bahwa konstruktor dipanggil setidaknya sekali untuk proxy dan sekali untuk kacang asli.
marcus
Perhatikan bahwa metode @PostConstruct tidak dipanggil saat halaman dimuat segera setelah server restart. (Ini mungkin bug JBoss.)
Dave Jarvis
96

Masalah utamanya adalah:

di konstruktor, injeksi dependensi belum terjadi *

* jelas tidak termasuk Injeksi Konstruktor


Contoh dunia nyata:

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

PENTING : @PostConstructdan @PreDestroy telah sepenuhnya dihapus di Jawa 11 .

Untuk tetap menggunakannya, Anda harus menambahkan Javax.annotation-api JAR ke dependensi Anda.

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>

Gradle

// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
Andrea Ligios
sumber
19
in a constructor, the injection of the dependencies has not yet occurred. benar dengan setter atau injeksi lapangan, tetapi tidak benar dengan injeksi konstruktor.
Adam Siemion
Dengan @PostConstruct dihapus di Java 11, bagaimana kita bisa menangani contoh dunia nyata ini dengan Java 11?
tet
@tet Seperti yang disebutkan dalam jawaban, Anda perlu menggunakan perpustakaan javax.annotation-api. Anotasi ini telah dihapus di Jawa 11, tetapi sudah ditandai ditinggalkan sejak Jawa 9.
narendra-choudhary
63

Jika kelas Anda melakukan semua inisialisasi di konstruktor, maka @PostConstructmemang berlebihan.

Namun, jika kelas Anda memiliki dependensi yang disuntikkan menggunakan metode setter, maka konstruktor kelas tidak dapat sepenuhnya menginisialisasi objek, dan kadang-kadang beberapa inisialisasi perlu dilakukan setelah semua metode setter dipanggil, maka kasus penggunaan @PostConstruct.

skaffman
sumber
@ staf: ditambah satu dari sisi saya. Jika saya ingin menginisialisasi bidang inputtext dengan nilai yang diambil dari database, saya dapat melakukannya dengan bantuan PostConstruct, tetapi gagal ketika mencoba melakukan hal yang sama di dalam konstruktor. Saya memiliki persyaratan ini untuk diinisialisasi tanpa menggunakan PostContruct. Jika Anda punya waktu, dapatkah Anda menjawabnya juga: stackoverflow.com/questions/27540573/…
Shirgill Farhan
10

Pertimbangkan skenario berikut:

public class Car {
  @Inject
  private Engine engine;  

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

Karena Mobil harus dipakai sebelum injeksi lapangan, mesin titik injeksi masih nol selama pelaksanaan konstruktor, menghasilkan NullPointerException.

Masalah ini dapat dipecahkan dengan JSR-330 Dependency Injection untuk Java constructor injection atau JSR 250 Annotations untuk anotasi metode Java @PostConstruct.

@PostConstruct

JSR-250 mendefinisikan seperangkat penjelasan umum yang telah dimasukkan dalam Java SE 6.

Anotasi PostConstruct digunakan pada metode yang perlu dijalankan setelah injeksi dependensi dilakukan untuk melakukan inisialisasi. Metode ini HARUS dipanggil sebelum kelas dimasukkan ke dalam layanan. Anotasi ini HARUS didukung pada semua kelas yang mendukung injeksi ketergantungan.

JSR-250 Chap. 2.5 javax.annotation.PostConstruct

Anotasi @PostConstruct memungkinkan definisi metode yang akan dieksekusi setelah instantiated dan semua injeksi telah dilakukan.

public class Car {
  @Inject
  private Engine engine;  

  @PostConstruct
  public void postConstruct() {
    engine.initialize();  
  }
  ...
} 

Alih-alih melakukan inisialisasi dalam konstruktor, kode tersebut dipindahkan ke metode yang dijelaskan dengan @PostConstruct.

Pemrosesan metode pasca-konstruksi adalah masalah sederhana untuk menemukan semua metode yang dijelaskan dengan @PostConstruct dan menjalankannya secara bergantian.

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

Pemrosesan metode pasca-konstruksi harus dilakukan setelah instantiasi dan injeksi selesai.

Humoyun Ahmad
sumber
1

Juga inisialisasi berdasarkan konstruktor tidak akan berfungsi sebagaimana dimaksud setiap kali semacam proxy atau remoting terlibat.

Ct akan dipanggil setiap kali EJB akan deserialized, dan setiap kali proxy baru dibuat untuk itu ...

struberg
sumber