Post

So sánh @Component, @Repository, @Service – Tìm hiểu chi tiết @Qualifier

1. @Component với @Repository và @Service

Trong hướng dẫn này, chúng ta sẽ tìm hiểu về sự khác biệt giữa các chú thích @Component, @Repository@Service trong Spring Framework.

Trong hầu hết các ứng dụng điển hình, chúng ta có các lớp riêng biệt như data access, presentation, service, business, … Và trong mỗi lớp, chúng ta có nhiều loại bean khác nhau. Nói một cách đơn giản, để phát hiện chúng một cách tự động, Spring sử dụng các chú thích scan classpath. Sau đó, nó đăng ký từng bean trong ApplicationContext.

Dưới đây là tổng quan nhanh về một số chú thích sau:

  • @Component là một khuôn mẫu chung cho bất kỳ thành phần nào do Spring quản lý
  • @Service chú thích các lớp dịch vụ
  • @Repository chú thích các lớp cố định - hoạt động như một kho lưu trữ cơ sở dữ liệu

Chúng ta đã có một bài viết giới thiệu về những chú thích này. Vì vậy, chúng ta sẽ chỉ tập trung vào sự khác biệt giữa chúng.

2. Làm rõ sự khác biệt

Sự khác biệt chính giữa các khuôn mẫu (stereotype) này là chúng được sử dụng để phân loại khác nhau. Khi chúng ta chú thích một lớp để auto-detection thì nên sử dụng khuôn mẫu tương ứng. Bây giờ, chúng ta hãy đi qua chúng chi tiết hơn.

@Component

Chúng ta có thể sử dụng @Component trên toàn ứng dụng để đánh dấu các bean là thành phần được quản lý của Spring. Spring chỉ chọn và đăng ký bean với @Component, không tìm kiếm @Service@Repository.

Chúng được đăng ký trong ApplicationContext vì bản thân chúng được chú thích bằng @Component:

1
2
3
4
5
6
7
@Component
public @interface Service {
}

@Component
public @interface Repository {
}

@Service@Repository là các trường hợp đặc biệt của @Component. Chúng giống nhau về mặt kỹ thuật nhưng chúng ta sử dụng chúng cho các mục đích khác nhau.

@ Repository

Công việc của @Repository là nắm bắt các ngoại lệ cụ thể và xử lý lại chúng như một trong những ngoại lệ chưa được kiểm tra của Spring.

Đối với việc cung cấp PersistenceExceptionTranslationPostProcessor của Spring sẽ yêu cầu thêm vào ngữ cảnh ứng dụng của chúng ta như sau:

1
2
<bean class=
"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

Bean trong bài viết này đã thêm một advisor cho bất kỳ bean nào được chú thích bằng @Repository.

@Service

Chúng ta đánh dấu bean bằng @Service để chỉ ra rằng nó nắm giữ logic nghiệp vụ. Vì vậy, không có bất kỳ đặc biệt nào khác ngoại trừ việc sử dụng nó trong lớp dịch vụ.

Việc hiểu cụ thể từng annotation là thực sự cần thiết, từ đó bạn có thể lựa chọn chú thích dựa trên các quy ước của chúng.

3. Tìm hiểu chi tiết @Qualifier Annotation

Trong phần còn lại của bài viết này, chúng ta sẽ khám phá chú thích @Qualifier xem nó giải quyết được những vấn đề gì và cách sử dụng nó như thế nào. Đồng thời, chúng ta cũng sẽ giải thích cách nó khác với chú thích @Primary và so với autowiring theo tên (name) nữa nhé.

Autowire cần cho Disambiguation

Chú thích @Autowired là một cách tuyệt vời để đưa một dependency vào Spring một cách rõ ràng trở nên thực sự cần thiết. Mặc dù nó hữu ích, nhưng có những trường hợp sử dụng mà chỉ riêng chú thích này là không đủ để Spring hiểu được bean nào inject.

Theo mặc định, Spring giải quyết các mục autowired theo loại (type).

Nếu có nhiều hơn một bean cùng loại trong container, Framework sẽ ném ngoại lệ NoUniqueBeanDefinitionException - cho biết rằng có nhiều hơn một bean có sẵn để autowiring.

Hãy tưởng tượng một tình huống trong đó có hai ứng viên có thể tồn tại để Spring đưa vào làm cộng tác viên của bean trong ví dụ cụ thể sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component("fooFormatter")
public class FooFormatter implements Formatter {
 
    public String format() {
        return "foo";
    }
}
 
@Component("barFormatter")
public class BarFormatter implements Formatter {
 
    public String format() {
        return "bar";
    }
}
 
@Component
public class FooService {
     
    @Autowired
    private Formatter formatter;
}

Nếu chúng ta cố gắng tải FooService vào context thì Spring sẽ ném ra một ngoại lệ NoUniqueBeanDefinitionException. Cái này là do Spring không biết bean nào để inject. Để tránh vấn đề này có một số giải pháp. Chú thích @Qualifier là một trong số đó.

@Qualifier Annotation

Bằng cách sử dụng chú thích @Qualifier, chúng ta có thể loại bỏ vấn đề bean nào cần được inject. Hãy xem lại ví dụ trước và xem cách chúng ta giải quyết vấn đề bằng cách sử dụng chú thích @Qualifier để cho biết chúng ta muốn sử dụng loại bean nào:

1
2
3
4
5
6
public class FooService {

	@Autowired
	@Qualifier("fooFormatter")
	private Formatter formatter;
}

Bằng cách sử dụng chú thích @Qualifier cùng với tên của triển khai cụ thể mà chúng ta muốn sử dụng - trong ví dụ này là Foo - có thể tránh được sự mơ hồ khi Spring tìm thấy nhiều bean cùng loại. Chúng ta cần lưu ý rằng tên qualifier được sử dụng là tên được khai báo trong chú thích @Component.

Lưu ý rằng chúng ta cũng có thể đã sử dụng chú thích @Qualifier trên các lớp triển khai Formatter, thay vì chỉ định tên trong chú thích @Component của chúng để có được hiệu quả tương tự:

1
2
3
4
5
6
7
8
9
10
11
@Component
@Qualifier("fooFormatter")
public class FooFormatter implements Formatter {
    // code gì đó
}
 
@Component
@Qualifier("barFormatter")
public class BarFormatter implements Formatter {
    // code gì đó
}

@Qualifier với @Primary

Có một chú thích khác được gọi là @Primary mà chúng ta có thể sử dụng để quyết định loại bean nào sẽ inject khi có sự mơ hồ liên quan đến dependency injection.

Chú thích này xác định tùy chọn khi có nhiều beancùng loại. Bean được liên kết với chú thích @Primary sẽ được sử dụng trừ khi có chỉ định khác. Hãy xem một ví dụ:

1
2
3
4
5
6
7
8
9
10
@Component
@Primary
public class FooFormatter implements Formatter {
    //...
}
 
@Component
public class BarFormatter implements Formatter {
    //...
}

Trong trường hợp này, chú thích @Primary được đặt trong một trong các lớp triển khai và sẽ có phân biệt kịch bản.

@Qualifier với Autowiring theo tên (name)

Một cách khác để quyết định giữa nhiều bean khi tạo tự động là sử dụng tên của trường để inject. Đây là mặc định trong trường hợp không có gợi ý nào khác cho Spring. Hãy xem đoạn code dựa trên ví dụ ban đầu của chúng ta:

1
2
3
4
5
public class FooService {
     
    @Autowired
    private Formatter fooFormatter;
}

Trong trường hợp này, Spring sẽ xác định rằng bean được đưa vào là FooFormatter vì tên trường được khớp với giá trị mà chúng ta đã sử dụng trong chú thích @Component cho bean đó.

Chúng ta đã mô tả chi tiết qua các tình huống mà chúng ta cho là cần xác định inject loại bean nào. Đặc biệt, chúng ta đã mô tả chú thích @Qualifier và so sánh nó với các cách tương tự khác để xác định loại bean nào cần được sử dụng.

Bài viết mang tính chất “ghi chú, lưu trữ, chia sẻ và phi lợi nhuận”.
Nếu bạn thấy hữu ích, đừng quên chia sẻ với bạn bè và đồng nghiệp của mình nhé!

Happy coding! 😎 👍🏻 🚀 🔥

Đọc thêm:

This post is licensed under CC BY 4.0 by the author.