【总结】 vs

Spring mvc3中的<context:annotation-config>和<context:component-scan>到底什么区别呢?平时是不是都在混用?今天看到一篇很不错的问答,解释的非常好。
原文地址如下: http://stackoverflow.com/questions/7414794/difference-between-contextannotation-config-vs-contextcomponent-scan

<context:annotation-config> 是用来 激活已经在spring配置文件applicationContext.xml中注册过的bean(不管这个bean是在xml中定义的,还是包含在扫描包中的)。

<context:component-scan> 能做<context:annotation-config>所能做的事,除此之外,<context:component-scan>还能找到并 注册在applicationContext.xml中配置的bean。

这么说很抽象,激活和注册的具体概念和区别又是什么呢?下面用一些小例子来说明个中的相同点和不同点。

现在有三个bean,A,B和C。类A中还注入了bean B和bean 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


这是我们想要的结果,但是这种方式太老旧了,接下来我们要用注释来代过段时间上述的xml配置。

首先,在类A中,我们加入@Autowired标签来注入属性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中bean A中的property(属性)了。
<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


有没有发现,跟我们预想的不一样,中间出了错(在类A中没有成功设值bean B和bean C)。
注释确实是个很方便的技术,但到目前为止,它们还没有发挥出功效。它们如同待执行的任务一样,需要有一个好的工具去找到它们并且让它们能真正的启动起来。

标签<context:annotation-config>就是派来解救注解的。此话怎讲呢,这个标签可以找到那些在applicationContext中定义过的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配置才对。于是我们接着把定义bean的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被标签autowired激活了,这样的结果出乎意料。

就如同文章一开始提到的,标签<context:annotation-config />的作用对象权权是那些已经在applicationContext中定义过的bean(但并没有激活)。当我们把三个定义的bean的xml语句移掉后,标签<context:annotation-config />就找不到“目标”了。

但这个问题并不存在于标签<context:component-scan>中,标签<context:component-scan>可以扫描指定的java包,找到“目标”。(就算bean没有在xml中定义也没关系)。
我们将xml中的配置修改如下:
<context:component-scan base-package="com.xxx" />


启动项目,得到了如下的输出:
creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff


好像少了类A的加载,为什么呢?
如果我们仔细观察这些类,会发现类A的java包是com.yyy,并不是标签<context:component-scan>中定义的com.xxx,所以这才是为什么漏掉整个A类的加载的原因。

为了修补这个bug,我们将base-package修改如下:
<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


至此,我们完成了符合注释风格的代码。

题外话,如果我们执意将base-package只定义为com.xxx,那有没有办法做到对类A的加载呢?那必须定义bean A。
<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并没有包含在扫描的java包中,标签<context:component-scan>还是会找到所有在applicationContext.xml中定义的其它所有bean(就像手动定义的beanA)。

那么,当我们在一个xml中同时配置标签<context:annotation-config />和<context:component-scan>,会发生什么呢?如下的配置会让类A重复加载吗?
<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 />会被忽略。

甚至当你定义同一个bean很多次,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






你可能感兴趣的:(annotation)