<context:annotation-config>
được sử dụng để kích hoạt các chú thích trong các bean đã được đăng ký trong ngữ cảnh ứng dụng (bất kể chúng được xác định bằng XML hay bằng cách quét gói).
<context:component-scan>
cũng có thể làm những gì <context:annotation-config>
mà <context:component-scan>
còn quét các gói để tìm và đăng ký đậu trong ngữ cảnh ứng dụng.
Tôi sẽ sử dụng một số ví dụ để hiển thị sự khác biệt / tương đồng.
Hãy bắt đầu với một thiết lập cơ bản của ba đậu các loại A
, B
và C
, với B
và C
được tiêm vàoA
.
package com.xxx;
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Với cấu hình XML sau:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A">
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
</bean>
Tải bối cảnh tạo ra đầu ra sau:
creating bean B: com.xxx.B@c2ff5
creating bean C: com.xxx.C@1e8a1f6
creating bean A: com.yyy.A@1e152c5
setting A.bbb with com.xxx.B@c2ff5
setting A.ccc with com.xxx.C@1e8a1f6
OK, đây là đầu ra dự kiến. Nhưng đây là "kiểu cũ" mùa xuân. Bây giờ chúng tôi có các chú thích vì vậy hãy sử dụng các chú thích để đơn giản hóa XML.
Đầu tiên, hãy tự động xác định bbb
và ccc
các thuộc tính trên bean A
như vậy:
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Điều này cho phép tôi xóa các hàng sau khỏi XML:
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
XML của tôi giờ đã được đơn giản hóa để:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
Khi tôi tải bối cảnh tôi nhận được đầu ra sau:
creating bean B: com.xxx.B@5e5a50
creating bean C: com.xxx.C@54a328
creating bean A: com.yyy.A@a3d4cf
OK, điều này là sai! Chuyện gì đã xảy ra? Tại sao tài sản của tôi không được tự động?
Chà, chú thích là một tính năng hay nhưng bản thân chúng không làm gì cả. Họ chỉ chú thích công cụ. Bạn cần một công cụ xử lý để tìm các chú thích và làm một cái gì đó với chúng.
<context:annotation-config>
để giải cứu. Điều này kích hoạt các hành động cho các chú thích mà nó tìm thấy trên các bean được xác định trong cùng bối cảnh ứng dụng nơi chính nó được xác định.
Nếu tôi thay đổi XML của mình thành này:
<context:annotation-config />
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
Khi tôi tải bối cảnh ứng dụng, tôi nhận được kết quả thích hợp:
creating bean B: com.xxx.B@15663a2
creating bean C: com.xxx.C@cd5f8b
creating bean A: com.yyy.A@157aa53
setting A.bbb with com.xxx.B@15663a2
setting A.ccc with com.xxx.C@cd5f8b
OK, điều này thật tuyệt, nhưng tôi đã xóa hai hàng khỏi XML và thêm một hàng. Đó không phải là một sự khác biệt quá lớn. Ý tưởng với các chú thích là nó nên xóa XML.
Vì vậy, hãy xóa các định nghĩa XML và thay thế tất cả chúng bằng các chú thích:
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xxx.B;
import com.xxx.C;
@Component
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
Trong khi trong XML, chúng tôi chỉ giữ điều này:
<context:annotation-config />
Chúng tôi tải bối cảnh và kết quả là ... Không có gì. Không có đậu được tạo ra, không có đậu được tự động. Không có gì!
Đó là bởi vì, như tôi đã nói trong đoạn đầu tiên, <context:annotation-config />
chỉ hoạt động trên các hạt đậu được đăng ký trong bối cảnh ứng dụng. Bởi vì tôi đã loại bỏ cấu hình XML cho ba bean, không có bean nào được tạo và <context:annotation-config />
không có "mục tiêu" nào để làm việc.
Nhưng đó sẽ không phải là vấn đề <context:component-scan>
có thể quét một gói để "mục tiêu" hoạt động. Hãy thay đổi nội dung của cấu hình XML thành mục sau:
<context:component-scan base-package="com.xxx" />
Khi tôi tải bối cảnh tôi nhận được đầu ra sau:
creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff
Hmmmm ... thiếu một cái gì đó. Tại sao?
Nếu bạn nhìn gần các lớp, lớp A
có gói com.yyy
nhưng tôi đã chỉ định trong <context:component-scan>
gói sử dụng, com.xxx
vì vậy lớp này hoàn toàn bỏ lỡ A
lớp của tôi và chỉ được chọn B
và C
nằm trong lớpcom.xxx
gói gói.
Để sửa lỗi này, tôi cũng thêm gói khác này:
<context:component-scan base-package="com.xxx,com.yyy" />
và bây giờ chúng tôi nhận được kết quả mong đợi:
creating bean B: com.xxx.B@cd5f8b
creating bean C: com.xxx.C@15ac3c9
creating bean A: com.yyy.A@ec4a87
setting A.bbb with com.xxx.B@cd5f8b
setting A.ccc with com.xxx.C@15ac3c9
Và đó là nó! Bây giờ bạn không còn định nghĩa XML nữa, bạn có chú thích.
Như một ví dụ cuối cùng, giữ lớp chú thích A
, B
và C
và thêm những điều sau đây để XML, những gì chúng ta sẽ nhận được sau khi tải bối cảnh?
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
Chúng tôi vẫn nhận được kết quả chính xác:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
Ngay cả khi bean cho lớp A
không thu được bằng cách quét, các công cụ xử lý vẫn được áp dụng bởi <context:component-scan>
trên tất cả các bean được đăng ký trong ngữ cảnh ứng dụng, ngay cả đối vớiA
khi được đăng ký thủ công trong XML.
Nhưng nếu chúng ta có XML sau đây, chúng ta sẽ nhận được các bản sao trùng lặp vì chúng ta đã chỉ định cả hai <context:annotation-config />
và <context:component-scan>
?
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
Không, không trùng lặp, chúng tôi lại nhận được kết quả như mong đợi:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
Đó là bởi vì cả hai thẻ đều đăng ký cùng một công cụ xử lý ( <context:annotation-config />
có thể được bỏ qua nếu <context:component-scan>
được chỉ định) nhưng Spring chỉ quan tâm đến việc chạy chúng một lần.
Ngay cả khi bạn tự đăng ký các công cụ xử lý nhiều lần, Spring vẫn sẽ đảm bảo chúng chỉ thực hiện phép thuật của chúng một lần; XML này:
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
<bean id="bla" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla1" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla2" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla3" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
vẫn sẽ tạo ra kết quả sau:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@25d2b2
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
OK, đó là về rap nó lên.
Tôi hy vọng thông tin này cùng với phản hồi từ @Tomasz Nurkiewicz và @Sean Patrick Floyd là tất cả những gì bạn cần để hiểu cách thức
<context:annotation-config>
và <context:component-scan>
hoạt động.