<context:annotation-config>
用于激活应用程序上下文中已注册的bean中的注释(无论它们是使用XML定义还是通过包扫描定义)。
<context:component-scan>
也可以执行<context:annotation-config>
的操作,但<context:component-scan>
还会扫描包以查找并注册应用程序上下文中的bean。
我将使用一些示例来显示差异/相似之处。
让我们从基本设置开始,其中三个类型为A
、B
和C
的bean被定义,B
和C
被注入到A
中。
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;
}
}
使用以下XML配置:
<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>
加载上下文会生成以下输出:
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
好的,这是预期的输出。但这是“旧式”的Spring。现在我们有注释,所以让我们使用它们来简化XML。
首先,让我们像这样自动装配bean A
上的bbb
和ccc
属性:
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;
}
}
这使我能够从 XML 中删除以下行:
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
我的XML现在被简化为:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
当我加载上下文时,我会得到以下输出:
creating bean B: com.xxx.B@5e5a50
creating bean C: com.xxx.C@54a328
creating bean A: com.yyy.A@a3d4cf
好的,这是错误的!发生了什么?为什么我的属性没有自动装配?
嗯,注解是一个不错的特性,但它们本身并不能做任何事情。它们只是注释而已。你需要一个处理工具来查找注解并对其进行操作。
<context:annotation-config>
来拯救。它会激活在同一应用程序上下文中定义的 bean 上找到的注解的操作。
如果我将我的 XML 更改为以下内容:
<context:annotation-config />
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
当我加载应用程序上下文时,我会得到正确的结果:
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
好的,这很不错,但我已经从XML中删除了两行并添加了一行。这并没有太大的区别。注释的想法是它应该可以替代XML。
因此,让我们删除XML定义,并用注释替换它们:
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;
}
}
在 XML 中,我们只保留这个:
<context:annotation-config />
我们加载了上下文,结果是...什么也没有。没有创建任何bean,也没有自动装配任何bean。什么都没有!
这是因为,正如我在第一段中所说的那样,<context:annotation-config />
只适用于在应用程序上下文中注册的bean。因为我删除了三个bean的XML配置,所以没有创建任何bean,<context:annotation-config />
没有“目标”可供使用。
但对于<context:component-scan>
来说,这不会成为问题,它可以扫描一个包以查找要处理的“目标”。让我们将XML配置的内容更改为以下条目:
<context:component-scan base-package="com.xxx" />
当我加载上下文时,我会得到以下输出:
creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff
嗯...好像有些东西丢失了。为什么呢?
如果你仔细看这些类,会发现类A
的包是com.yyy
,但我在<context:component-scan>
中指定使用的是com.xxx
包,所以完全忽略了我的A
类,只选择了B
和C
,它们在com.xxx
包中。
为了解决这个问题,我还需要添加另一个包:
<context:component-scan base-package="com.xxx,com.yyy" />
现在我们得到了预期的结果:
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
就是这样!现在你不再需要XML定义,而是使用注释。
最后举个例子,保留带有注释的类A
、B
和C
,并将以下内容添加到XML中,在加载上下文后我们会得到什么?
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
我们仍然得到了正确的结果:
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
即使类A的bean没有通过扫描获得,处理工具仍然会被应用于应用程序上下文中注册的所有bean,即使是在XML中手动注册的A。
但是,如果我们有以下XML,我们会得到重复的bean吗?因为我们同时指定了和?
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
不,没有重复项,我们再次获得了预期的结果:
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
这是因为两个标签都注册了相同的处理工具(如果指定了<context:component-scan>
,则可以省略<context:annotation-config />
),但Spring只会运行它们一次。
即使您自己多次注册处理工具,Spring仍然会确保它们只执行一次;以下是XML示例:
<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" />
仍将生成以下结果:
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
好的,就这样了。
我希望这些信息以及 @Tomasz Nurkiewicz 和 @Sean Patrick Floyd 的回答足以让您了解 <context:annotation-config>
和 <context:component-scan>
的工作方式。
component-scan
。 - Jerry Chin