Mục lục
Spring dependency injection là một trong những tính năng cốt lỗi của Spring Framework cho phép khởi tạo, quản lý và lắp ráp các bean trong toàn ứng dụng. Để sử dụng những tính năng này chúng ta phải thông qua các annotation được cung cấp trong org.springframework.beans.factory.annotation and org.springframework.context.annotation package. Chúng có thể được xem là những annotation cốt lỗi của Spring mà chúng ta sẽ cùng nhau tìm hiểu ngay sau đây.
Những annotation cốt lỗi trong Spring
@Autowired
Chúng ta sử dụng @Autowired để chú thích một dependency sẽ được Spring sẽ giải quyết và tiêm nó vào từ Spring IoC container. Chúng ta có thể sử dụng @Autowired với constructor, setter hoặc trên một thuộc tính cụ thể.
@Autowired trên constructor
class Car { Engine engine; @Autowired Car(Engine engine) { this.engine = engine; } }
@Autowired trên setter
class Car { Engine engine; @Autowired void setEngine(Engine engine) { this.engine = engine; } }
@Autowired trên thuộc tính
class Car { @Autowired Engine engine; }
Với tất cả các cách trên thì Spring sẽ cố gắng tìm một bean có kiểu dữ liệu là Engine để tiêm vào Car class. Bên cạnh đó, @Autowired có một thuộc tính required có giá trị mặc định là TRUE, khi Spring tìm không thấy một bean tương ứng để tiêm vào nó sẽ ném ra một exception.
Lưu ý kể từ Spring phiên bản 4.3 chúng ta không cần chú thích @Autowired trên constructor, Spring sẽ tự hiểu và tìm các dependency tương ứng để tiêm vào. Tuy nhiên nếu có hơn 1 constructor thì chúng ta cần chỉ định @Autowired trên constructor được dùng để khai báo các dependency cần sử dụng trong Class.
@Bean
@Bean được dùng để chú thích một method dùng để khởi tạo một Spring bean.
@Bean Engine engine() { return new Engine(); }
Spring sẽ gọi những method này để khởi tạo một bean với kiểu dữ liệu tương ứng khi cần thiết. Mặc định, tên của bean sẽ giống với tên của method, nếu bạn muốn đổi tên của bean thì có thể truyền một tham số vào @Bean annotation để chỉ định tên của bean.
@Bean("engine") Engine getEngine() { return new Engine(); }
Lưu ý rằng tất cả các method được chú thích bằng @Bean phải nằm trong các class @Configuration.
@Qualifiter
Chúng ta thường sử dụng @Qualifier với @Autowired để cung cấp Bean ID hay Bean Name mà chúng ta muốn sử dụng trong trường hợp có nhiều hơn 1 bean có cùng kiểu dữ liệu trong Spring IoC container.
Ví dụ trường hợp chúng ta có 2 implement của một interface
class Bike implements Vehicle {} class Car implements Vehicle {}
Trong trường hợp này chúng ta phải cung cấp Bean Name cho Spring biết đâu là dependency mà bạn muốn Spring tiêm vào. Chúng ta có thể cung cấp Bean Name với @Qualifier annotation như sau:
@Autowired Biker(@Qualifier("bike") Vehicle vehicle) { this.vehicle = vehicle; }
Hoặc nếu sử dụng setter injection
@Autowired void setVehicle(@Qualifier("bike") Vehicle vehicle) { this.vehicle = vehicle; }
@Autowired @Qualifier("bike") Vehicle vehicle;
@Required
@Required trên setter method để chú thích các dependency 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.
@Value
Chúng ta có thể sử dụng @Value để đưa giá trị của một thuộc tính vào Bean .Nó tương thích với constructor, setter và field injection.
// constructor Engine(@Value("8") int cylinderCount) { this.cylinderCount = cylinderCount; } //setter @Autowired void setCylinderCount(@Value("8") int cylinderCount) { this.cylinderCount = cylinderCount; } hay @Value("8") void setCylinderCount(int cylinderCount) { this.cylinderCount = cylinderCount; } Field @Value("8") int cylinderCount;
Tuy nhiên với cách sử dụng trên thì giá trị của các thuộc tính là hằng số và không thể thay đổi trong quá trình chạy ứng dụng.
Thông thường @Value được sử dụng với một key định nghĩa trong các tệp cấu hình .properties or .yaml cùng với giá trị tương ứng. Các thuộc tính được chú thích với @Value({key}) sẽ mang giá trị tương ứng trong các tệp cấu hình.
Ví dụ trong application.properties được nghĩa cặp key-value sau
engine.fuelType=petrol
Chúng ta có thể đưa giá trị của engine.fuelType vào một thuộc tính như sau:
@Value("${engine.fuelType}") String fuelType;
@DependsOn
Chúng ta có thể sử dụng annotation này trên một bean để thông báo với Spring rằng phải khởi tạo một bean khác trước khi khởi tạo nó với bean name được chỉ định trong @DependsOn.
@Bean @DependsOn("fuel") Engine engine() { return new Engine(); }
@Lazy
Sử dụng @Lazy sẽ khiến Spring chỉ khởi tạo một bean khi cần thiết, nghĩa là khi có một request đến ứng dụng cần sử dụng đến bean này. Bởi vì mặc định thì Spring sẽ khởi tạo tất cả các singleton bean tại thời điểm ứng dụng khởi chạy.
Sử dụng @Lazy tại những nơi khác nhau sẽ có những ảnh hưởng khác nhau như:
- Chung với @Bean annotation trên một method để trì hoãn việc Spring gọi đến method để khởi tạo bean lúc chương trình khởi chạy.
- Chung với @Configuration class và tất cả các method được chú thích với @Bean annotation sẽ bị trì hoãn.
- Trên @Component class sẽ trì hoãn việc Spring quét và khởi tạo ban đầu.
- Trên một @Autowired constructor, setter hoặc field để tải các dependency khi cần thiết.
Ví dụ tạo ra một @Configuration chỉ được khởi tạo khi cần thiết và một @Bean method khởi tạo bean ngay khi @Configuration này được khởi tạo.
@Configuration @Lazy class VehicleFactoryConfig { @Bean @Lazy(false) Engine engine() { return new Engine(); } }
@Primary
Đôi lúc chúng ta có nhiều bean cùng kiểu dữ liệu một lúc, khi khai báo dependency chúng ta cần chỉ định rõ là cần dùng bean nào. Trong trường hợp này chúng ta có thể giải quyết bằng cách sử dụng @Qualifier với tên của bean cần sử dụng. Tuy nhiên thật phiền phức khi lúc nào cũng phải khai báo chúng với @Qualifier annotation.
Giả sử có 2 bean cùng kiểu dữ liệu, nhưng hầu như chúng ta chỉ sử dụng 1 bean theo mặc định và một bean còn lại rất ít khi sử dụng. Thì chúng ta có thể chú thích bean sử dụng nhiều với @Primary annotation. Lúc này nếu khai báo dependency không có @Qualifier thì @Primary bean sẽ được sử dụng.
@Component @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ên thì Driver sẽ sử dụng Car bean trong khi Biker sẽ sử dụng Bike bean.
@Scope
@Scope được dùng để định nghĩa phạm vi của một @Component class hay một @Bean method trong Spring. Các phạm vi có thể có là singleton, prototype, request, session, globalSession, và một số phạm vi được tuỳ biến,
@Component @Scope("prototype") class Engine {}
Context Configuration Annotations
Chúng ta có một số annotation được dùng để cấu hình application context như
@Profile
Nếu chúng ta muốn Spring sử dụng @Component class và @Bean method với một số profile nhất định. Chúng ta có thể chú thích chúng @Profile annotaion chỉ định rõ chúng chỉ được khởi tạo khi profile tương ứng được active.
@Component @Profile("sportDay") class Bike implements Vehicle {}
@Import
Chúng ta có thể sử dụng các class @Configuration cụ thể mà không cần quét thành phần với chú thích này. Chúng tôi có thể cung cấp các lớp đó với đối số giá trị của @Import:
@Import(VehiclePartSupplier.class) class VehicleFactoryConfig {}
@ImportResource
Chúng ta có thể nhập các cấu hình XML với chú thích này. Chúng tôi 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 {}
@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 repeating annotations 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 {}
@PropertySources
Chúng tôi có thể sử dụng chú thích 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ừ 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.
Nguồn tham khảo