ทำไมต้องใช้ @PostConstruct


294

ใน bean ที่@PostConstructถูกจัดการจะถูกเรียกใช้หลังจากตัวสร้างอ็อบเจ็กต์ Java ปกติ

ทำไมฉันจะใช้@PostConstructเพื่อเริ่มต้นโดยถั่วแทนนวกรรมิกตัวเอง?


4
finalผมประทับใจที่ฉีดคอนสตรัคเป็นที่นิยมโดยทั่วไปจะช่วยให้การอ้างอิงที่จะเป็น เมื่อพิจารณาถึงรูปแบบนั้นเหตุใดจึง@PostConstructถูกเพิ่มใน J2EE - พวกเขาต้องเห็นกรณีการใช้งานอื่นอย่างแน่นอน?
mjaggard

คำตอบ:


409
  • เพราะเมื่อมีการเรียกตัวสร้างถั่วยังไม่ได้เริ่มต้น - นั่นคือไม่มีการพึ่งพาการฉีด ใน@PostConstructวิธีการนั้นถั่วจะเริ่มต้นได้อย่างสมบูรณ์และคุณสามารถใช้การอ้างอิง

  • เพราะนี่คือสัญญาที่รับประกันว่าวิธีนี้จะถูกเรียกใช้เพียงครั้งเดียวในวงจรชีวิตของถั่ว มันอาจเกิดขึ้น (แต่ไม่น่าเป็นไปได้) ว่าถั่วถูกสร้างอินสแตนซ์หลายครั้งโดยคอนเทนเนอร์ในการทำงานภายใน แต่รับประกันได้ว่า@PostConstructจะถูกเรียกใช้เพียงครั้งเดียว


17
ในกรณีที่คอนสตรัคตัวเองอัตโนมัติทุกการอ้างอิง - จากนั้นถั่วยังสามารถเริ่มต้นได้อย่างเต็มที่ในการสร้าง (หลังจากตั้งค่าฟิลด์ทั้งหมดอัตโนมัติด้วยตนเอง)
yair

7
เกิดอะไรขึ้นในกรณีที่คอนสตรัคเตอร์ของถั่วอาจถูกเรียกมากกว่าหนึ่งครั้ง
yair

1
อาจเป็นอะไรบางอย่างเช่น "ทู่" หากคอนเทนเนอร์ตัดสินใจที่จะเก็บ bean บนที่เก็บดิสก์จากนั้นเรียกคืนจากที่นั่น
Bozho

13
ไม่ใช่ว่าไม่น่าจะเห็นนวกรรมิกเรียกหลายครั้ง เมื่อคอนเทนเนอร์ instantiates พร็อกซีคุณจะเห็นคอนสตรัคเตอร์นั้นถูกเรียกอย่างน้อยหนึ่งครั้งสำหรับพร็อกซีและอีกครั้งสำหรับ real bean
มาร์คัส

โปรดทราบว่าวิธีการ @PostConstruct จะไม่ถูกเรียกเมื่อโหลดหน้าเว็บทันทีหลังจากเซิร์ฟเวอร์รีสตาร์ท (นี่อาจเป็นข้อผิดพลาด JBoss)
เดฟจาร์วิส

96

หลักปัญหาคือว่า:

ในนวกรรมิกการฉีดของการพึ่งพายังไม่เกิดขึ้น *

* ไม่รวม Constructor Injection


ตัวอย่างโลกแห่งความจริง:

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 ได้รับสมบูรณ์ลบออกในชวา 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>

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 เมื่อ

ด้วย @PostConstruct ถูกลบใน Java 11 ตอนนี้เราจะจัดการตัวอย่างโลกแห่งความเป็นจริงด้วย Java 11 ได้อย่างไร
Tet

@tet ตามที่ระบุไว้ในคำตอบคุณต้องใช้ไลบรารี javax.annotation-api คำอธิบายประกอบเหล่านี้ถูกลบใน Java 11 แต่ถูกทำเครื่องหมายว่าเลิกใช้แล้วตั้งแต่ Java 9
narendra-choudhary

63

หากคลาสของคุณทำการเริ่มต้นทั้งหมดใน Constructor นั้น@PostConstructจะซ้ำซ้อนแน่นอน

อย่างไรก็ตามหากคลาสของคุณมีการพึ่งพาการฉีดโดยใช้เมธอด setter คอนสตรัคเตอร์ของคลาสจะไม่สามารถเริ่มต้นวัตถุได้อย่างสมบูรณ์และบางครั้งการกำหนดค่าเริ่มต้นบางอย่างจำเป็นต้องดำเนินการหลังจากเรียกเมธอด setter ทั้งหมด@PostConstructแล้ว


@staffman: บวกหนึ่งจากด้านข้างของฉัน หากฉันต้องการเริ่มต้นเขตข้อมูล inputtext ด้วยค่าที่ดึงมาจากฐานข้อมูลฉันสามารถทำมันด้วยความช่วยเหลือของ PostConstruct แต่ล้มเหลวเมื่อพยายามทำแบบเดียวกันภายในนวกรรมิก ฉันมีข้อกำหนดนี้เพื่อเริ่มต้นโดยไม่ต้องใช้ PostContruct หากคุณมีเวลาคุณสามารถตอบคำถามนี้ได้เช่น: stackoverflow.com/questions/27540573/…
Shirgill Farhan

10

พิจารณาสถานการณ์สมมติต่อไปนี้:

public class Car {
  @Inject
  private Engine engine;  

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

เนื่องจากต้องให้อินสแตนซ์ของรถยนต์ก่อนการฉีดภาคสนามเครื่องยนต์จุดฉีดจึงยังคงว่างในระหว่างการทำงานของตัวสร้างซึ่งส่งผลให้ NullPointerException

ปัญหานี้สามารถแก้ไขได้โดยJSR-330 Dependency Injection สำหรับ Java constructor injection หรือ JSR 250 คำอธิบายประกอบทั่วไปสำหรับคำอธิบายประกอบวิธีการ 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);
        }
      });
}

การประมวลผลของวิธีการหลังการก่อสร้างจะต้องดำเนินการหลังจาก instantiation และฉีดเสร็จสมบูรณ์


1

การกำหนดค่าเริ่มต้นตามคอนสตรัคเตอร์จะไม่ทำงานตามที่ตั้งใจเมื่อใดก็ตามที่เกี่ยวข้องกับพร็อกซีหรือ remoting บางชนิด

ct จะถูกเรียกเมื่อใดก็ตามที่ EJB ถูก deserialized และเมื่อใดก็ตามที่มีการสร้างพรอกซีใหม่สำหรับมัน ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.