我正在学习 Spring 3,并且似乎不太了解<context:annotation-config>
和<context:component-scan>
背后的功能。
从我阅读的内容来看,它们似乎处理不同的注释( @Required
, @Autowired
等与@Component
Component, @Service
Repository, @Repository
等),但是从我阅读的内容来看,它们注册了相同的bean 后处理器类。
更让我感到困惑的是, <context:component-scan>
annotation-config
属性。
有人可以阐明这些标签吗?有什么相似之处,有什么不同之处,一个被另一个取代,它们彼此完成,我是否需要其中一个?
<context:annotation-config>
用于激活已经在应用程序上下文中注册的 bean 中的注释(无论它们是使用 XML 定义还是通过包扫描定义的)。
<context:component-scan>
也可以执行<context:annotation-config>
操作,但是<context:component-scan>
也会扫描软件包以在应用程序上下文中查找并注册 bean。
我将使用一些示例来显示差异 / 相似之处。
让我们从基本设置三个类型为A
, B
和C
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
好的,这是预期的输出。但这是 “旧风格” 的春天。现在我们有了注释,因此让我们使用它们来简化 XML。
首先,让我们自动装配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,处理工具仍会由<context:component-scan>
应用于在应用程序上下文中注册的所有 Bean,即使对于已在 XML 中手动注册的A
但是,如果我们具有以下 XML,将会得到重复的 bean,因为我们同时指定了<context:annotation-config />
和<context:component-scan>
吗?
<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>
工作方式所需要的。
我发现了关于哪些注释是由哪些声明选择的一个很好的摘要。通过研究,您会发现<context:component-scan/>
<context:annotation-config/>
识别的注释的超集,即:
@Component
, @Service
, @Repository
, @Controller
, @Endpoint
@Configuration
, @Bean
, @Lazy
, @Scope
, @Order
, @Primary
, @Profile
, @DependsOn
, @Import
, @ImportResource
如您所见, <context:component-scan/>
/> 具有 CLASSPATH 组件扫描和 Java @Configuration 功能,从逻辑上扩展了<context:annotation-config/>
Spring 使您可以做两件事:
1. 自动接线
通常,您在 applicationContext.xml 中定义 bean,而其他 bean 是使用构造函数或 setter 方法连接的。您可以使用 XML 或注释对 bean 进行接线。如果使用批注,则需要激活批注,并且必须在applicationContext.xml 中<context:annotation-config />
。这将简化applicationContext.xml 中标记的结构,因为您不必手动连接 bean(构造函数或 setter)。您可以使用@Autowire
注释,并且将按类型连接 bean。
转义手动 XML 配置的步骤是
2. 自动发现
从某种意义上讲,您甚至不需要在 applicationContext.xml 中<bean>
标记,自动发现使 XML 进一步简化了一步。您只需使用以下注释之一标记特定的 bean,Spring 就会自动将标记的 bean 及其依赖项连接到 Spring 容器中。注释如下: @Controller , @ Service,@Component , @Repository 。通过使用<context:component-scan>
并指向基本包,Spring 将自动发现并将组件连接到 Spring 容器中。
作为结论:
<context:annotation-config />
用于能够使用@Autowired注释<context:component-scan />
用于确定特定 bean 的搜索以及自动装配的尝试。