我正在学习Spring 3,但似乎并没有掌握<context:annotation-config>and背后的功能<context:component-scan>。
<context:annotation-config>and
<context:component-scan>
从我读过他们似乎处理不同的注解(@Required,@Autowired等等VS @Component,@Repository,@Service等),而且从我读过他们注册相同什么bean后置处理器类。
@Required,@Autowired
VS @Component,@Repository,@Service
为了更迷惑我,还有一个annotation-config 属性上<context:component-scan>。
annotation-config
有人可以阐明这些标签吗?有什么相似之处,有什么不同之处,一个被另一个取代,它们彼此完成,我是否需要其中一个?
<context:annotation-config>用于激活已经在应用程序上下文中注册的bean中的注释(无论它们是使用XML定义还是通过包扫描定义的)。
<context:annotation-config>
<context:component-scan>不仅可以执行<context:annotation-config>操作,<context:component-scan>还可以扫描软件包以在应用程序上下文中查找并注册bean。
我将使用一些示例来显示差异/相似之处。
让我们用型的三种豆基本设置开始A,B并且C,与B和C被注入A。
A
B
C
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的bbb和ccc属性,A如下所示:
bean
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
嗯…有些东西丢失了。为什么?
如果你在班closelly看,类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,即使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" />
不,没有重复,我们再次得到了预期的结果:
这是因为两个标签都注册了相同的处理工具(如果已指定,则可以省略),但是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