问题
我正在学习Spring 3,我似乎没有掌握<context:annotation-config>
和<context:component-scan>
背后的功能。
根据我的阅读,他们似乎处理不同的注释(@ Required,@ Autowired等vs @Component,@ Repository,@ Service等),但是从我读过的内容中他们注册了相同的bean后处理器类。
为了让我更加困惑,在<context:component-scan>
上有一个annotation-config
属性。
有人可以对这些标签有所了解吗?什么是相似的,什么是不同的,一个被另一个取代,它们相互完成,我是否需要其中一个,两者兼而有之?
#1 热门回答(1281 赞)
<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
好的,这是预期的输出。但这是春天的“老风格”。现在我们有注释,所以让我们使用它们来简化XML。
首先,让我们在beanA
上自动装配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 class并且只获取
com.xxx包中的
B和
C`。
为了解决这个问题,我还添加了另一个包:
<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上的
<context:component-scan>应用,即使对于手动注册的
A`也是如此在XML中。
但是,如果我们有以下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>
的工作原理。
#2 热门回答(155 赞)
我找到了这个很好的889191989,其中的注释被哪些声明所取代。通过研究它,你会发现<context:component-scan />
识别<context:annotation-config />
识别的注释的超集,即:
- @Component,@ Service,@ Repository,@ Controller,@ Endpoint
- @Configuration,@ Bean,@ Lazy,@ Scope,@ Order,@ Primary,@ Profile,@ DependsOn,@ Import,@ ImportResource
正如你所看到的那样``context:component-scan />logically**使用CLASSPATH组件扫描和Java @Configuration功能扩展了**
<context:annotation-config />`。
#3 热门回答(81 赞)
Spring允许你做两件事:
- 豆类自动装配
- 自动发现bean
1.自动装配
通常inapplicationContext.xmlyou使用构造函数或setter方法定义bean和其他bean。你可以使用XML或注释来连接bean。如果你使用注释,则需要激活注释,并且必须添加<context:annotation-config />
inapplicationContext.xml。这将简化标签fromapplicationContext.xml的结构,因为你不必手动连接bean(构造函数或setter)。你可以使用@Autowire
注释,bean将按类型连接。
转义手动XML配置的一步是
2.自动发现
自动发现进一步简化了XML,因为你甚至不需要在inapplicationContext.xml中添加<bean>
标记。你只需使用以下注释之一标记特定bean,Spring将自动将标记的bean及其依赖项连接到Spring容器中。注释如下:@ Controller,@ Service,@ Component,@ Repository。通过使用<context:component-scan>
并指向基础包,Spring将自动发现并将组件连接到Spring容器中。
作为结论:
- <context:annotation-config />用于使用@Autowired注释
- <context:component-scan />用于确定特定bean的搜索和自动装配的尝试。