Tại sao nên sử dụng @PostConstruct?


294

Trong một bean được quản lý, @PostConstructđược gọi sau hàm tạo đối tượng Java thông thường.

Tại sao tôi sẽ sử dụng @PostConstructđể khởi tạo bằng bean, thay vì chính hàm tạo thông thường?


4
Tôi có ấn tượng rằng tiêm constructor thường được ưa thích để cho phép phụ thuộc final. Với mô hình đó, tại sao lại @PostConstructđược thêm vào J2EE - chắc chắn họ đã thấy một trường hợp sử dụng khác chắc chắn?
mjaggard

Câu trả lời:


409
  • bởi vì khi hàm tạo được gọi, bean chưa được khởi tạo - tức là không có phần phụ thuộc nào được thêm vào. Trong @PostConstructphương thức, bean được khởi tạo hoàn toàn và bạn có thể sử dụng các phụ thuộc.

  • bởi vì đây là hợp đồng đảm bảo rằng phương thức này sẽ chỉ được gọi một lần trong vòng đời của bean. Có thể xảy ra (mặc dù không chắc) rằng một bean được khởi tạo nhiều lần bởi container trong hoạt động bên trong của nó, nhưng nó đảm bảo rằng nó @PostConstructsẽ chỉ được gọi một lần.


17
trong trường hợp hàm tạo tự động tự động tất cả các phụ thuộc - thì bean cũng có thể được khởi tạo hoàn toàn trong hàm tạo (sau khi thiết lập thủ công tất cả các trường tự động).
Yair

7
trường hợp nào mà hàm tạo của bean có thể được gọi nhiều lần?
Yair

1
Có lẽ một cái gì đó như "thụ động". Nếu container quyết định lưu trữ bean trên lưu trữ đĩa và sau đó khôi phục nó từ đó.
Bozho

13
Nó không phải là không thể nhìn thấy các nhà xây dựng được gọi nhiều lần. Khi container khởi tạo một proxy, bạn sẽ thấy hàm tạo đó được gọi ít nhất một lần cho proxy và một lần cho bean thực.
marcus

Lưu ý rằng các phương thức @PostConstruct không được gọi khi trang được tải ngay sau khi máy chủ khởi động lại. (Đây có thể là một lỗi JBoss.)
Dave Jarvis

96

Các chính vấn đề là:

trong một hàm tạo, việc tiêm phụ thuộc chưa xảy ra *

* rõ ràng không bao gồm Con Contortor tiêm


Ví dụ thực tế:

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

QUAN TRỌNG : @PostConstruct@PreDestroy đã bị xóa hoàn toàn trong Java 11 .

Để tiếp tục sử dụng chúng, bạn sẽ cần thêm JAR javax.annotation-api vào phần phụ thuộc của mình.

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>

Học sinh lớp

// 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. đúng với setter hoặc tiêm trường, nhưng không đúng với tiêm constructor.
Adam Siemion

Với @PostConstruct bị xóa trong Java 11, làm thế nào bây giờ chúng ta có thể xử lý ví dụ thế giới thực này với Java 11?
tet

@tet Như đã đề cập trong câu trả lời, bạn cần sử dụng thư viện javax.annotation-api. Các chú thích này đã bị xóa trong Java 11, nhưng đã bị đánh dấu không dùng nữa kể từ Java 9.
narendra-choudhary

63

Nếu lớp của bạn thực hiện tất cả các khởi tạo của nó trong hàm tạo, thì @PostConstructthực sự là dư thừa.

Tuy nhiên, nếu lớp của bạn có các phụ thuộc được chèn bằng các phương thức setter, thì hàm tạo của lớp không thể khởi tạo hoàn toàn đối tượng và đôi khi một số khởi tạo cần được thực hiện sau khi tất cả các phương thức setter được gọi, do đó là trường hợp sử dụng @PostConstruct.


@staffman: cộng với một từ phía tôi. Nếu tôi muốn khởi tạo trường inputtext với giá trị được tìm nạp từ cơ sở dữ liệu, tôi có thể làm điều đó với sự trợ giúp của PostConstruct, nhưng không thành công khi cố gắng thực hiện tương tự bên trong hàm tạo. Tôi có yêu cầu này để khởi tạo mà không cần sử dụng PostConturation. Nếu bạn có thời gian, bạn có thể vui lòng trả lời câu hỏi này không: stackoverflow.com/questions/27540573/iêu
Shirgill Farhan 22/12/14

10

Hãy xem xét kịch bản sau đây:

public class Car {
  @Inject
  private Engine engine;  

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

Do Xe phải được khởi tạo trước khi tiêm trường, động cơ điểm tiêm vẫn không có giá trị trong quá trình thực thi của hàm tạo, dẫn đến NullPulumException.

Vấn đề này có thể được giải quyết bằng cách tiêm phụ thuộc JSR-330 cho phương thức tiêm của trình xây dựng Java hoặc Chú thích chung của JSR 250 cho chú thích phương thức Java @PostConstruct.

@PostConstruct

JSR-250 định nghĩa một tập hợp các chú thích phổ biến đã được đưa vào Java SE 6.

Chú thích PostConstruct được sử dụng trên một phương thức cần được thực thi sau khi thực hiện tiêm phụ thuộc để thực hiện bất kỳ khởi tạo nào. Phương thức này PHẢI được gọi trước khi lớp được đưa vào phục vụ. Chú thích này PHẢI được hỗ trợ trên tất cả các lớp hỗ trợ tiêm phụ thuộc.

JSR-250 Chap. 2.5 javax.annotation.PostConstruct

Chú thích @PostConstruct cho phép định nghĩa các phương thức được thực thi sau khi thể hiện đã được khởi tạo và tất cả các phép tiêm đã được thực hiện.

public class Car {
  @Inject
  private Engine engine;  

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

Thay vì thực hiện khởi tạo trong hàm tạo, mã được chuyển sang một phương thức được chú thích bằng @PostConstruct.

Việc xử lý các phương thức hậu xây dựng là một vấn đề đơn giản là tìm tất cả các phương thức được chú thích bằng @PostConstruct và lần lượt gọi chúng.

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

Việc xử lý các phương pháp sau xây dựng phải được thực hiện sau khi hoàn thành việc tiêm và tiêm.


1

Ngoài ra, khởi tạo dựa trên hàm tạo sẽ không hoạt động như dự định mỗi khi có một số loại ủy quyền hoặc từ xa có liên quan.

Các ct sẽ được gọi bất cứ khi nào một EJB được khử lưu huỳnh và bất cứ khi nào một proxy mới được tạo cho nó ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.