在Spring中,很多人對<context:annotation-config>和<context:component-scan>的區別不是很清楚,下面我們詳細討論它們的區別所在。
<context:annotation-config> 是用於激活那些已經在spring容器裏註冊過的bean(無論是通過xml的方式還是通過package sanning的方式)上面的註解。
<context:component-scan>除了具有
<context:annotation-config>的功能之外,
<context:component-scan>
還可以在指定的package下掃描以及註冊java
bean 。
下面我們通過例子來詳細查看他們的區別,
有三個class 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;
}
}
在applicationContext.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>
加載applicationContext.xml配置文件,將得到下面的結果:
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, 這個結果沒什麼好說的,就是完全通過xml的方式,不過太過時了,下面通過註解的方式來簡化我們的xml配置文件
首先,我們使用autowire的方式將對象bbb和ccc注入到A中:
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;
}
}
然後,我們就可以從applicationContext.xml中移除下面的配置
<property
name="bbb"
ref="bBean"/>
<property
name="ccc"
ref="cBean"/>
移除之後,我們的applicationContext.xml配置文件就簡化爲下面的樣子了
<bean
id="bBean"class="com.xxx.B"/>
<bean
id="cBean"class="com.xxx.C"/>
<bean
id="aBean"class="com.yyy.A"/>
當我們加載applicationContext.xml配置文件之後,將得到下面的結果:
creating
bean B:
com.xxx.B@5e5a50
creating
bean C:
com.xxx.C@54a328
creating
bean A:
com.yyy.A@a3d4cf
OK, 結果是錯誤的的,究竟是因爲什麼呢?爲什麼我們的屬性沒有被注入進去呢?
是因爲註解本身並不能夠做任何事情,它們只是最基本的組成部分,我們需要能夠處理這些註解的處理工具來處理這些註解
這就是<context:annotation-config>
所做的事情
我們將applicationContext.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"/>
當我們加載applicationContext.xml配置文件之後,將得到下面的結果:
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, 結果正確了
但是如果我們將代碼作如下修改:
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;
}
}
applicationContext.xml配置文件修改爲:
<context:annotation-config/>
當我們加載applicationContext.xml配置文件之後,卻沒有任何輸出,這是爲什麼呢?
那是因爲<context:annotation-config />僅能夠在已經在已經註冊過的bean上面起作用。
對於沒有在spring容器中註冊的bean,它並不能執行任何操作。
但是不用擔心,<context:component-scan>除了具有
<context:annotation-config
/>的功能之外,還具有自動將帶有@component,@service,@Repository等註解的對象註冊到spring容器中的功能。
我們將applicationContext.xml配置文件作如下修改:
<context:component-scan
base-package="com.xxx"/>
當我們加載applicationContext.xml的時候,會得到下面的結果:
creating
bean B:
com.xxx.B@1be0f0a
creating
bean C:
com.xxx.C@80d1ff
這是什麼原因呢?
是因爲我們僅僅掃描了com.xxx包及其子包的類,而class A是在com.yyy包下,所以就掃描不到了
下面我們在applicationContext.xml中把com.yyy也加入進來:
<context:component-scan
base-package="com.xxx"/>
<context:component-scan
base-package="com.xxx,com.yyy"/>
然後加載applicationContext.xml就會得到下面的結果:
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
哇,結果正確啦 !
回頭看下我們的applicationContext.xml文件,已經簡化爲:
<context:component-scan
base-package="com.xxx"/>
<context:component-scan
base-package="com.xxx,com.yyy"/>
了。
那如果我們在applicationContext.xml手動加上下面的配置,也就是說既在applicationContext.xml中手動的註冊了A的實例對象,同時,通過component-scan去掃描並註冊B,C的對象
<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
雖然class A並不是通過掃描的方式註冊到容器中的 ,但是<context:component-scan>
所產生的的處理那些註解的處理器工具,會處理所有綁定到容器上面的bean,不管是通過xml手動註冊的還是通過scanning掃描註冊的。
那麼,如果我們通過下面的方式呢?我們既配置了<context:annotation-config />,又配置了<context:component-scan base-package="com.xxx" />,它們都具有處理在容器中註冊的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:annotation-config
/>和
<context:component-scan>同時存在的時候,前者會被忽略。也就是那些@autowire,@resource等注入註解只會被注入一次
哪怕是你手動的註冊了多個處理器,spring仍然只會處理一次:
<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