Chào bạn, trong bài viết hôm nay chúng ta sẽ nói cách sử dụng Annotation để cấu hình cho dự án Spring.
Kể từ version Spring 2.5 trở đi chúng ta có thể cấu hình các DI bằng cách sử dụng các Annotaion thay cho cấu hình bằng file XML. Chúng ta dùng Annotation thay cho cấu hình XML vì cấu hình XML khá cồng kềnh, dài dòng và phức tạp.
Chức năng autowire (nhúng bean phụ thuộc DI) không bật lên mặc định mà chúng ta phải khai báo và bật nó lên trước khi sử dụng các Annotation để nhúng các bean. Để bật chức năng này lên ta khai báo như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
Chúng ta khai báo
Spring cung cấp một số Annotation sau.
@Required annotation nói rằng các method set phải bắt buộc phải nhúng bean phụ thuộc vào.
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class EmailClient {
private EmailService emailService;
@Required
public void setEmailService(EmailService emailService) {
this.emailService = emailService
}
}
Chúng ta sử dụng @Autowire để nhúng một bean phụ thuộc vào bean. Có thể nhúng qua constructor, setter hoặc qua các biến.
1
2
3
4
5
6
7
8
class Car {
private Engine engine;
@Autowired
Car(Engine engine) {
this.engine = engine;
}
}
1
2
3
4
5
6
7
8
class Car {
private Engine engine;
@Autowired
void setEngine(Engine engine) {
this.engine = engine;
}
}
1
2
3
4
5
6
7
8
class Car {
private Engine engine;
@Autowired
void setEngine(Engine engine) {
this.engine = engine;
}
}
Sử dụng @Primary khi ta có nhiều beans cùng kiểu dữ liệu và ta muốn ưu tiên một kiểu nào đó. Trong ví dụ dưới đây ta viết 1 chương trình gửi thư. Có 2 dịch vụ gửi thư là Facebook và Email.
Cả 2 services FacebookMessageService và EmailMessageService cùng cài đặt chung 1 interface MessageService. Như vậy ta có 2 bean FacebookMessageService và EmailMessageService có cùng kiểu dữ liệu là MessageService. Vậy cái nào sẽ được ưu tiên đầu tiên. Chúng ta sẽ đi tiếp ví dụ sau.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.javaguides.spring</groupId>
<artifactId>spring-primary-annotation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-scope-example</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
1
2
3
public interface MessageService {
public void sendMsg();
}
1
2
3
4
5
6
7
8
9
import org.springframework.stereotype.Component;
@Component
public class FacebookMessageService implements MessageService {
@Override
public void sendMsg() {
System.out.println("gửi bằng facebook");
}
}
import org.springframework.stereotype.Component;
1
2
3
4
5
6
7
8
9
@Primary
@Component
public class EmailMessageService implements MessageService {
@Override
public void sendMsg() {
System.out.println("gửi bằng email");
}
}
Như vậy ta thấy rằng cả 2 service FacebookMessageService và EmailMessageService cùng implement 1 interface là MessageService.
Nhưng ta thấy lớp EmailMessageService có thêm Annotation là @Primary ở đây.
1
2
3
4
5
6
7
8
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "net.javaguides.spring.primary")
public class AppConfig {
}
1
2
3
4
5
6
7
8
9
10
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MessageService messageService = context.getBean(MessageService.class);
messageService.sendMsg();
context.close();
}
}
Kết quả là : gửi bằng email được in ra. Bởi vì khi ta gọi getBean(MessageService.class) nó sẽ ưu tiên EmailMessageService vì lớp này có annotation là @Primary. Nên nó ưu tiên dùng thằng này, mặc dù EmailMessageService và FacebookMessageService cùng kiểu dữ liệu là MessageService
Chúng ta sử dụng @Qualifier để xác định chính xác loại dữ liệu nào được đưa vào. Như các em thấy ví dụ @Primary chúng ta có EmailMessageService và FacebookMessageService có cùng 1 kiểu dữ liệu là MessageService. Vì chúng cùng kiểu nên khi nhúng bean vào thì Spring IoC không biết nên nhúng cái nào là đúng. Nên để giúp Spring IoC nhúng đúng bean thì ta cần dùng @Qualifier để chỉ ra bean nhúng vào chính là EmailMessageService hay FacebookMessageService
1
2
3
4
5
6
7
@Autowired
@Qualifier("emailMessageService")
private MessageService emailMessageService;
@Autowired
@Qualifier("facebookMessageService")
private MessageService facebookMessageService;
Trong bài vòng đời của bean. Chúng ta sử dụng InitializingBean, DisposableBean để can thiệp vào bean lúc tạo và phá huỷ. Ngoài cách đó chúng ta có thể sử dụng annotation @PostConstruct and @PreDestroy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
public class DatabaseInitializer {
private List < User > listOfUsers = new ArrayList < > ();
@PostConstruct
public void init() {
User user = new User(1, "User");
User user1 = new User(2, "Admin");
User user2 = new User(3, "SuperAdmin");
listOfUsers.add(user);
listOfUsers.add(user1);
listOfUsers.add(user2);
System.out.println("-----------List of users added in init() method ------------");
for (Iterator < User > iterator = listOfUsers.iterator(); iterator.hasNext();) {
User user3 = (User) iterator.next();
System.out.println(user3.toString());
}
// save to database
}
@PreDestroy
public void destroy() {
// Delete from database
listOfUsers.clear();
System.out.println("-----------After of users removed from List in destroy() method ------------");
for (Iterator < User > iterator = listOfUsers.iterator(); iterator.hasNext();) {
User user3 = (User) iterator.next();
System.out.println(user3.toString());
}
}
}