Spring Core Annotations

Người dịch : Nguyễn Xuân Trường - Học viên lớp Java08
Email liên hệ: truongnx.work@gmail.com
Bài viết gốc: https://www.baeldung.com/spring-core-annotations

Bài viết này nằm trong series Spring Annotations
Spring Core Annotations (bài viết hiện tại)
Spring Web Annotations
SpringBoot Annotations

1. Tổng quan

Chúng ta có thể tận dụng các khả năng của Spring Dependency Injection engine bằng cách sử dụng các annotation trong gói org.springframework.beans.factory.annotationorg.springframework.context.annotation.
Chúng ta thường gọi đây là “Spring Core Annotations” và chúng ta sẽ xem xét chúng trong bài viết này.

2. Các annotation liên quan đến Dependency Injection

2.1. @Autowired

Chúng ta có thể sử dụng @Autowired để đánh dấu một dependency mà Spring sẽ giải quyết và đưa vào. Chúng ta có thể sử dụng annotation này với một constructor, setter hoặc chèn trường.
Chèn vào constructor:

class Car {
    Engine engine;

    @Autowired
    Car(Engine engine) {
        this.engine = engine;
    }
}

Chèn vào Setter:

class Car {
    Engine engine;

    @Autowired
    void setEngine(Engine engine) {
        this.engine = engine;
    }
}

Chèn trường:

class Car {
    @Autowired
    Engine engine;
}

@Autowired có một đối số bắt buộc boolean với giá trị mặc định là true. Nó điều chỉnh hành vi của Spring khi nó không tìm thấy một Bean thích hợp để kết nối. Khi true, một ngoại lệ được ném ra, nếu không, không có gì được kết nối.
Lưu ý rằng nếu chúng ta sử dụng chèn vào constructor, thì tất cả các đối số của constructor là bắt buộc.
Từ phiên bản 4.3, chúng ta không cần chú thích các constructor với @Autowired một cách rõ ràng trừ khi chúng ta khai báo ít nhất hai constructor.

2.1. @Bean

@Bean đánh dấu một phương thức mà khởi tạo một Spring bean:

@Bean
Engine engine() {
    return new Engine();
}

Spring gọi các phương thức này khi một phiên bản mới của kiểu trả về được yêu cầu.
Kết quả của Bean có cùng tên với phương thức tạo Bean. Nếu chúng ta muốn đặt tên khác, chúng ta có thể làm như sau với tên hoặc các giá trị đối số của annotation này (giá trị đối số là alias cho tên đối số):

@Bean("engine")
Engine getEngine() {
    return new Engine();
}

Lưu ý rằng tất cả các phương thức được chú thích bằng @Bean phải nằm trong các class @Configuration.

2.3. @Qualifier

Chúng tôi sử dụng @Qualifier cùng với @Autowired để cung cấp id bean hoặc tên bean mà chúng ta muốn sử dụng trong các tình huống không rõ ràng.
Ví dụ: hai bean sau triển khai cùng một interface:

class Bike implements Vehicle {}

class Car implements Vehicle {}

Nếu Spring cần chèn một Bban Vehicle, nó sẽ có nhiều định nghĩa phù hợp. Trong những trường hợp như vậy, chúng tôi có thể cung cấp tên bean một cách rõ ràng bằng cách sử dụng chú thích @Qualifier.
Sử dụng chèn constructor:

@Autowired
Biker(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

Sử dụng chèn Setter:

@Autowired
void setVehicle(@Qualifier("bike") Vehicle vehicle) {
    this.vehicle = vehicle;
}

Ngoài ra:

@Autowired
@Qualifier("bike")
void setVehicle(Vehicle vehicle) {
    this.vehicle = vehicle;
}

Sử dụng chèn trường:

@Autowired
@Qualifier("bike")
Vehicle vehicle;

2.4. @Required

@Required trên các phương thức Setter để đánh dấu các phần phụ thuộc mà chúng ta muốn đưa vào thông qua XML:

@Required
void setColor(String color) {
    this.color = color;
}
<bean class="com.baeldung.annotations.Bike">
    <property name="color" value="green" />
</bean>

Nếu không, BeanInitializationException sẽ được ném ra.

2.5. @Value

Chúng ta có thể sử dụng @Value để đưa các giá trị thuộc tính vào bean. Nó tương thích với constructor, setter và field injection.
Chèn vào constructor:

Engine(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Chèn vào Setter:

@Autowired
void setCylinderCount(@Value("8") int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Ngoài ra:

@Value("8")
void setCylinderCount(int cylinderCount) {
    this.cylinderCount = cylinderCount;
}

Chèn trường:

@Value("8")
int cylinderCount;

Tất nhiên, việc chèn các giá trị static không hữu ích. Do đó, chúng ta có thể sử dụng các placeholders strings trong @Value để chuyển các giá trị được xác định trong các nguồn bên ngoài, ví dụ: trong các tệp .properties hoặc .yaml.
Hãy giả sử tệp .properties sau:

engine.fuelType=petrol

Chúng ta có thể chèn giá trị của engine.fuelType vào như sau:

@Value("${engine.fuelType}")
String fuelType;

2.6. @DependsOn

Chúng ta có thể sử dụng annotation này để làm cho Spring khởi tạo các bean khác trước annotation. Thông thường, hành vi này là tự động, dựa trên sự phụ thuộc rõ ràng giữa các bean.
Chúng tôi chỉ cần annotation này khi các phụ thuộc là ngầm định, ví dụ: tải trình điều khiển JDBC Driver hoặc khởi tạo biến static.
Chúng ta có thể sử dụng @DependsOn trên class phụ thuộc chỉ định tên của các bean phụ thuộc. Đối số giá trị của annotation cần một mảng chứa các tên bean phụ thuộc:

@DependsOn("engine")
class Car implements Vehicle {}

Ngoài ra, nếu chúng ta xác định bean bằng chú thích @Bean, thì phương thức tạo bean sẽ được chú thích bằng @DependsOn:

@Bean
@DependsOn("fuel")
Engine engine() {
    return new Engine();
}

2.7. @Lazy

Chúng ta sử dụng @Lazy khi chúng ta muốn khởi tạo bean của mình ‘lười biếng’. Theo mặc định, Spring tạo tất cả các singleton bean khi khởi động / khởi động cấu trúc của ứng dụng.
Tuy nhiên, có những trường hợp chúng ta cần tạo bean khi có yêu cầu chứ không phải lúc khởi động ứng dụng.
Chú thích này hoạt động khác nhau tùy thuộc vào nơi chúng ta đặt. Chúng ta có thể đặt nó trên:

  • một @Bean chú thực một phương thức khởi bean, để trì hoãn việc gọi phương thức
  • một class @Configuration và tất cả các phương thức @Bean chứa trong đó sẽ bị ảnh hưởng
  • một class @Component, không phải là class @Configuration, bean này sẽ được khởi tạo một cách ‘lười biếng’
  • một @Autowired constructor, setter, hoặc trường để tải dependency của một cách lười biếng (thông qua proxy)
    Annotation này có một đối số được đặt tên là value với giá trị mặc định là true. Sẽ rất hữu ích khi ghi đè hành vi mặc định.
    Ví dụ: đánh dấu các bean được tải nhanh khi thiết lập chung hoặc thiết cấu hình các phương thức @Bean cụ thể để tải nhanh trong class @Configuration được đánh dấu bằng @Lazy:
@Configuration
@Lazy
class VehicleFactoryConfig {

    @Bean
    @Lazy(false)
    Engine engine() {
        return new Engine();
    }
}

2.8. @Lookup

Một phương thức được chú thích bằng @Lookup yêu cầu Spring trả về một phiên của kiểu trả về của phương thức khi chúng ta gọi nó.

2.9. @Primary

Đôi khi chúng ta cần xác định nhiều bean cùng loại. Trong những trường hợp này, việc chèn sẽ không thành công vì Spring không biết chúng ta cần bean nào.
Chúng đã thấy một tùy chọn để giải quyết tình huống này: đánh dấu tất cả các điểm liên kết bằng @Qualifier và chỉ định tên của bean được yêu cầu.
Tuy nhiên, hầu hết thời gian chúng ta cần một loại cụ thể và hiếm khi những loại khác. Chúng ta có thể sử dụng @Primary để đơn giản hóa trường hợp này: nếu chúng tađánh dấu bean được sử dụng thường xuyên nhất bằng @Primary, nó sẽ được chọn trên các điểm chèn mà không đủ tiêu chuẩn:

@Primary
class Car implements Vehicle {}

@Component
class Bike implements Vehicle {}

@Component
class Driver {
    @Autowired
    Vehicle vehicle;
}

@Component
class Biker {
    @Autowired
    @Qualifier("bike")
    Vehicle vehicle;
}

Trong ví dụ trước Car là phương tiện chính. Do đó, trong class Driver, Spring sẽ chèn một Car bean. Tất nhiên, trong Biker bean, giá trị của trường vehicle sẽ là đối tượng Bike vì nó đủ tiêu chuẩn.

2.10. @Scope

Chúng ta sử dụng @Scope để xác định phạm vi của class @Component hoặc định nghĩa @Bean. Nó có thể là singleton, prototype, request, session, globalSession hoặc một số phạm vi tùy chỉnh.
Ví dụ:

@Component
@Scope("prototype")
class Engine {}

3. Nội dung cấu hình Annotation

Chúng ta có thể cấu hình nội dung ứng dụng bằng các annotation được mô tả trong phần này.

3.1. @Profile

Nếu chúng ta muốn Spring chỉ sử dụng class @Component hoặc một phương thức @Bean khi một cấu hình cụ thể đang hoạt động, chúng ta có thể đánh dấu nó bằng @Profile. Chúng ta có thể định cấu hình tên của cấu hình bằng đối số value của annotation:

@Component
@Profile("sportDay")
class Bike implements Vehicle {}

3.2. @Import

Chúng ta có thể sử dụng các class @Configuration cụ thể mà không cần quét qua component với annotation này. Chúng tôi có thể cung cấp các class đó với đối số @ Import value:

@Import(VehiclePartSupplier.class)
class VehicleFactoryConfig {}

3.3. @ImportResource

Chúng tôi có thể nhập các cấu hình XML với annotation này. Chúng ta có thể chỉ định các vị trí tệp XML bằng đối số vị trí hoặc bằng bí danh của nó, đối số giá trị:

@Configuration
@ImportResource("classpath:/annotations.xml")
class VehicleFactoryConfig {}

3.4. @PropertySource

Với annotation này, chúng tôi có thể xác định các tệp thuộc tính cho cài đặt ứng dụng:

@Configuration
@PropertySource("classpath:/annotations.properties")
class VehicleFactoryConfig {}

@PropertySource tận dụng tính năng lặp lại annotation của Java 8, có nghĩa là chúng ta có thể đánh dấu một lớp bằng nó nhiều lần:

@Configuration
@PropertySource("classpath:/annotations.properties")
@PropertySource("classpath:/vehicle-factory.properties")
class VehicleFactoryConfig {}

3.5. @PropertySources

Chúng ta có thể sử dụng annotation này để chỉ định nhiều cấu hình @PropertySource:

@Configuration
@PropertySources({ 
    @PropertySource("classpath:/annotations.properties"),
    @PropertySource("classpath:/vehicle-factory.properties")
})
class VehicleFactoryConfig {}

Lưu ý rằng kể từ phiên bản Java 8, chúng ta có thể đạt được điều tương tự với tính năng chú thích lặp lại như được mô tả ở trên.

4. Tổng kết

Trong bài viết này, chúng ta đã xem tổng quan về các Annotation Core phổ biến nhất của Spring. Chúng ta đã thấy cách cấu hình hệ thống bean và nội dung ứng dụng cũng như cách đánh dấu các lớp để quét component.