GVKun编程网logo

匹配的通配符是严格的,但是找不到元素'context:component-scan的声明

13

本文将为您提供关于匹配的通配符是严格的,但是找不到元素'context:component-scan的声明的详细介绍,同时,我们还将为您提供关于和之间的区别、applicationContext.xm

本文将为您提供关于匹配的通配符是严格的,但是找不到元素'context:component-scan的声明的详细介绍,同时,我们还将为您提供关于之间的区别、applicationContext.xml中的区别、context:annotation-config 和 context:component-scan的实用信息。

本文目录一览:

匹配的通配符是严格的,但是找不到元素'context:component-scan的声明

匹配的通配符是严格的,但是找不到元素'context:component-scan的声明

尝试我的第一个春季项目时遇到以下错误:

Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element ''context:component-scan

这是applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:p="http://www.springframework.org/schema/p"       xmlns:aop="http://www.springframework.org/schema/aop"       xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">        <context:component-scan base-package="com.xyz" /></beans>

是什么导致错误?

答案1

小编典典

您尚未指定上下文名称空间的架构位置,这是此特定错误的原因:

<beans .....  xmlns:context="http://www.springframework.org/schema/context"  xsi:schemaLocation="    http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd    http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx-2.5.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config><context:component-scan


<context:annotation-config> 是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解,是一个注解处理工具。

<context:component-scan>除了具有<context:annotation-config>的功能之外,<context:component-scan>还可以在指定的package下扫描以及注册javabean 。

 

下面我们通过例子来详细查看他们的区别,

有三个class   A,B,C,并且B,C的对象被注入到A中.

 

[java] view plain copy 在CODE上查看代码片派生到我的代码片

  1. package com.xxx;  

  2. public class B {  

  3.   public B() {  

  4.     System.out.println("creating bean B: " + this);  

  5.   }  

  6. }  

  7.   

  8. package com.xxx;  

  9. public class C {  

  10.   public C() {  

  11.     System.out.println("creating bean C: " + this);  

  12.   }  

  13. }  

  14.   

  15. package com.yyy;  

  16. import com.xxx.B;  

  17. import com.xxx.C;  

  18. public class A {   

  19.   private B bbb;  

  20.   private C ccc;  

  21.   public A() {  

  22.     System.out.println("creating bean A: " + this);  

  23.   }  

  24.   public void setBbb(B bbb) {  

  25.     System.out.println("setting A.bbb with " + bbb);  

  26.     this.bbb = bbb;  

  27.   }  

  28.   public void setCcc(C ccc) {  

  29.     System.out.println("setting A.ccc with " + ccc);  

  30.     this.ccc = ccc;   

  31.   }  

  32. }  

 

 

在applicationContext.xml中加入下面的配置 :

 

<bean id="bBean"/><bean id="cBean"/><bean id="aBean">
  <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中:

 

 

[java] view plain copy 在CODE上查看代码片派生到我的代码片

  1. package com.yyy;  

  2. import org.springframework.beans.factory.annotation.Autowired;  

  3. import com.xxx.B;  

  4. import com.xxx.C;  

  5. public class A {   

  6.   private B bbb;  

  7.   private C ccc;  

  8.   public A() {  

  9.     System.out.println("creating bean A: " + this);  

  10.   }  

  11.   @Autowired  

  12.   public void setBbb(B bbb) {  

  13.     System.out.println("setting A.bbb with " + bbb);  

  14.     this.bbb = bbb;  

  15.   }  

  16.   @Autowired  

  17.   public void setCcc(C ccc) {  

  18.     System.out.println("setting A.ccc with " + ccc);  

  19.     this.ccc = ccc;  

  20.   }  

  21. }  

 

 

 

然后,我们就可以从applicationContext.xml中移除下面的配置

 

<property name="bbb" ref="bBean"/><property name="ccc" ref="cBean"/>

 

移除之后,我们的applicationContext.xml配置文件就简化为下面的样子了

 

<bean id="bBean"/><bean id="cBean"/><bean id="aBean"/>

 

当我们加载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"/><bean id="cBean"/><bean id="aBean"/>

 

当我们加载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, 结果正确了。


下面演示<context:annotation-config> 跟 <context:component-scan>区别:

但是如果我们将代码作如下修改:

 

 

[java] view plain copy 在CODE上查看代码片派生到我的代码片

  1. package com.xxx;  

  2. import org.springframework.stereotype.Component;  

  3. @Component  

  4. public class B {  

  5.   public B() {  

  6.     System.out.println("creating bean B: " + this);  

  7.   }  

  8. }  

  9.   

  10. package com.xxx;  

  11. import org.springframework.stereotype.Component;  

  12. @Component  

  13. public class C {  

  14.   public C() {  

  15.     System.out.println("creating bean C: " + this);  

  16.   }  

  17. }  

  18.   

  19. package com.yyy;  

  20. import org.springframework.beans.factory.annotation.Autowired;  

  21. import org.springframework.stereotype.Component;  

  22. import com.xxx.B;  

  23. import com.xxx.C;  

  24. @Component  

  25. public class A {   

  26.   private B bbb;  

  27.   private C ccc;  

  28.   public A() {  

  29.     System.out.println("creating bean A: " + this);  

  30.   }  

  31.   @Autowired  

  32.   public void setBbb(B bbb) {  

  33.     System.out.println("setting A.bbb with " + bbb);  

  34.     this.bbb = bbb;  

  35.   }  

  36.   @Autowired  

  37.   public void setCcc(C ccc) {  

  38.     System.out.println("setting A.ccc with " + ccc);  

  39.     this.ccc = ccc;  

  40.   }  

  41. }  

 

 

 

 

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"/>

 

结果仍是正确的:

 

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"/>

 

不用担心,不会出现的:

 

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仍然只会处理一次:

 

[xml] view plain copy 在CODE上查看代码片派生到我的代码片

  1. <context:annotation-config />  

  2. <context:component-scan base-package="com.xxx" />  

  3. <bean id="aBean"  />  

  4. <bean id="bla"  />  

  5. <bean id="bla1"  />  

  6. <bean id="bla2"  />  

  7. <bean id="bla3"  />  

 

 

结果仍是正确的:

 

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



<context:annotation-config>和<context:component-scan>之间的区别

之间的区别

我正在学习Spring 3,但似乎并没有掌握<context:annotation-config>and背后的功能<context:component-scan>

从我读过他们似乎处理不同的注解(@Required,@Autowired等等VS @Component,@Repository,@Service等),而且从我读过他们注册相同什么bean后置处理器类。

为了更迷惑我,还有一个annotation-config 属性上<context:component-scan>

有人可以阐明这些标签吗?有什么相似之处,有什么不同之处,一个被另一个取代,它们彼此完成,我是否需要其中一个?

答案1

小编典典

<context:annotation-config>用于激活已经在应用程序上下文中注册的bean中的注释(无论它们是使用XML定义还是通过包扫描定义的)。

<context:component-scan>不仅可以执行<context:annotation-config>操作,<context:component-scan>还可以扫描软件包以在应用程序上下文中查找并注册bean。

我将使用一些示例来显示差异/相似之处。

让我们用型的三种豆基本设置开始AB并且C,与BC被注入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"/><bean id="cBean"/><bean id="aBean">  <property name="bbb" ref="bBean" />  <property name="ccc" ref="cBean" /></bean>

加载上下文会产生以下输出:

creating bean B: com.xxx.B@c2ff5creating bean C: com.xxx.C@1e8a1f6creating bean A: com.yyy.A@1e152c5setting A.bbb with com.xxx.B@c2ff5setting A.ccc with com.xxx.C@1e8a1f6

好的,这是预期的输出。但这是“旧风格”的spring。现在我们有了注释,因此让我们使用它们来简化XML。

首先,让我们自动连接beanbbbccc属性,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;  }}

这使我可以从XML中删除以下行:

<property name="bbb" ref="bBean" /><property name="ccc" ref="cBean" />

我的XML现在简化为:

<bean id="bBean"/><bean id="cBean"/><bean id="aBean"/>

当我加载上下文时,我得到以下输出:

creating bean B: com.xxx.B@5e5a50creating bean C: com.xxx.C@54a328creating bean A: com.yyy.A@a3d4cf

好,这是错误的!发生了什么?为什么我的房屋不自动接线?

好吧,注解是一个不错的功能,但就其本身而言,它们什么也没做。他们只是注释东西。你需要一个处理工具来查找批注并对其进行处理。

<context:annotation-config>进行营救。这将激活对在定义自身的相同应用程序上下文中定义的bean上发现的注释的操作。

如果我将XML更改为此:

<context:annotation-config /><bean id="bBean"/><bean id="cBean"/><bean id="aBean"/>

当我加载应用程序上下文时,我得到正确的结果:

creating bean B: com.xxx.B@15663a2creating bean C: com.xxx.C@cd5f8bcreating bean A: com.yyy.A@157aa53setting A.bbb with com.xxx.B@15663a2setting A.ccc with com.xxx.C@cd5f8b

好的,这很好,但是我从XML中删除了两行并添加了一行。那不是很大的区别。带有注释的想法是应该删除XML。

因此,让我们删除XML定义并将其全部替换为注释:

package com.xxx;import org.springframework.stereotype.Component;@Componentpublic class B {  public B() {    System.out.println("creating bean B: " + this);  }}package com.xxx;import org.springframework.stereotype.Component;@Componentpublic 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;@Componentpublic 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@1be0f0acreating 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@cd5f8bcreating bean C: com.xxx.C@15ac3c9creating bean A: com.yyy.A@ec4a87setting A.bbb with com.xxx.B@cd5f8bsetting A.ccc with com.xxx.C@15ac3c9

就是这样!现在你不再有XML定义,而有了注释。

作为最后一个示例,保留带注释的类A,B并将C以下内容添加到XML中,加载上下文后会得到什么?

<context:component-scan base-package="com.xxx" /><bean id="aBean"/>

我们仍然得到正确的结果:

creating bean B: com.xxx.B@157aa53creating bean C: com.xxx.C@ec4a87creating bean A: com.yyy.A@1d64c37setting A.bbb with com.xxx.B@157aa53setting 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"/>

不,没有重复,我们再次得到了预期的结果:

creating bean B: com.xxx.B@157aa53creating bean C: com.xxx.C@ec4a87creating bean A: com.yyy.A@1d64c37setting A.bbb with com.xxx.B@157aa53setting A.ccc with com.xxx.C@ec4a87

这是因为两个标签都注册了相同的处理工具(如果已指定,则可以省略),但是Spring只会运行它们一次。

即使你多次注册处理工具,Spring仍将确保它们仅执行一次魔术。此XML:

<context:annotation-config /><context:component-scan base-package="com.xxx" /><bean id="aBean"/><bean id="bla"/><bean id="bla1"/><bean id="bla2"/><bean id="bla3"/>

仍会产生以下结果:

creating bean B: com.xxx.B@157aa53creating bean C: com.xxx.C@ec4a87creating bean A: com.yyy.A@25d2b2setting A.bbb with com.xxx.B@157aa53setting A.ccc with com.xxx.C@ec4a87

applicationContext.xml中<context:annotation-config> 和 <context:component-scan>的区别

applicationContext.xml中的区别

Difference between <context:annotation-config> vs <context:component-scan>

<context:annotation-config>是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解。

<context:component-scan>除了具有<context:annotation-config>的功能之外,<context:component-scan>还可以在指定的package下扫描以及注册javabean。

下面我们通过例子来详细查看他们的区别,

有三个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
ottom: 1em; border: 0px; vertical-align: baseline; clear: both; font-family: Arial,结果是错误的的,究竟是因为什么呢?为什么我们的属性没有被注入进去呢?

是因为注解本身并不能够做任何事情,它们只是最基本的组成部分,我们需要能够处理这些注解的处理工具来处理这些注解

这就是<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"/>

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
ottom: 1em; border: 0px; vertical-align: baseline; clear: both; font-family: Arial,结果正确了

但是如果我们将代码作如下修改:

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容器中的功能。

<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 basepackage="com.xxx,com.yyy"/><context:component-scan base-package="com.xxx"/> 
然后加载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"/>

了。

那如果我们在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"/>

不用担心,不会出现的:

因为<context:annotation-config />和<context:component-scan>同时存在的时候,前者会被忽略。也就是那些@autowire,@resource等注入注解只会被注入一次

哪怕是你手动的注册了多个处理器,spring仍然只会处理一次:

<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean"/>
<bean id="bla"/>
<bean id="bla1"/>
<bean id="bla2"/>
<bean id="bla3"/>

结果仍是正确的:

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

context:annotation-config 和 context:component-scan

context:annotation-config 和 context:component-scan

一、XML 配置方式

         现在有三个类 ClassA、ClassB 和 ClassC,ClassA 和 ClassB、ClassC 不在同一个包下,其中 ClassA 中有两个引用属性 classB 和 classC,它们分别是 ClassB 和 ClassC 的实例。

package com.codeproject.jackie.springdive.beanconfig.yyy;
public class ClassB {
  public ClassB() {
    System.out.println("creating instance of ClassB :" + this);
  }
}

package com.codeproject.jackie.springdive.beanconfig.yyy;
public class ClassC {
  public ClassC() {
    System.out.println("creating instance of ClassC :" + this);
  }
}

package com.codeproject.jackie.springdive.beanconfig.xxx;
import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
public class ClassA {
  private ClassB classB;
  private ClassC classC;
  public ClassA() {
    System.out.println("creating instance of ClassA :" + this);
  }
  public void setClassB(ClassB classB) {
    System.out.println("setting field classB with " + classB + " in ClassA");
    this.classB = classB;
  }
  public void setClassC(ClassC classC) {
    System.out.println("setting field classC with " + classC + " in ClassA");
    this.classC = classC;
  }
}

    下面是相应的 XML 配置:   

<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA">
  <property name="classB" ref="classB" />
  <property name="classC" ref="classC" />
</bean>

         加载 Spring 容器后输出以下结果:

creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@10d09ad3
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@4178460d
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3f3f210f
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@10d09ad3 in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@4178460d in ClassA

二、注解配置方式

         使用注解就是为了简化 XML 配置,下面对上面的例子做些改动。在 ClassA 中的 setter 方法上增加 @Autowired 注解。

package com.codeproject.jackie.springdive.beanconfig.xxx;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
       import org.springframework.beans.factory.annotation.Autowired;
       public class ClassA {
           private ClassB classB;
           private ClassC classC;
           public ClassA() {
               System.out.println("creating instance of ClassA :" + this);
           }
           @Autowired
           public void setClassB(ClassB classB) {
               System.out.println("setting field classB with " + classB + " in ClassA");
               this.classB = classB;
           }
           @Autowired
           public void setClassC(ClassC classC) {
               System.out.println("setting field classC with " + classC + " in ClassA");
               this.classC = classC;
           }
       }
          XML 配置文件修改如下:   
<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           再次加载 Spring 容器,输出以下结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@48082d37
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@10aadc97
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@4178460d

          从结果中我们可以发现属性 classB 和 classC 没有自动注入到 ClassA 中。

        这是因为我们还缺少能够找到并处理这些注解的工具 --- 注解处理器,注解本身并不能够做任何其它事情,它们只是用来注释一些东西。

三、<context:annotation-config>

         为了让 Spring 能够自动装配属性 classB 和 classC 以解决上面的问题,可以在 XML 文件注册 AutowiredAnnotationBeanPostProcessor 这个 Bean 后处理器。

<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
            通常我们会采用一种更优雅的方式 --- 即使用 <context:annotation-config>。

        <context:annotation-config> 会隐式地向 Spring 容器中 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor 以及 RequiredAnnotationBeanPostProcessor 这四个 Bean 后处理器。注册这四个 Bean 后处理器的作用是为了在系统中识别并激活相应的注解, @Autowired、@Resource @PostConstruct@PreDestroy、@PersistenceContext 以及 @Required 等注解

        现在将 XML 配置文件修改如下:

<context:annotation-config />
<bean id="classB" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassB" />
<bean id="classC" class="com.codeproject.jackie.springdive.beanconfig.yyy.ClassC" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           再次加载 Spring 容器,输出结果如下:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@236527f
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@59556d12 
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@1a0fced4 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@236527f in ClassA 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@59556d12 in ClassA
          输出的确是我们想要的结果。但是 XML 配置文件并没有简化多少。下面我删掉 XML 中的 bean 声明并使用用 @Component 注解来替换它们。      
package com.codeproject.jackie.springdive.beanconfig.yyy;
       import org.springframework.stereotype.Component;
       @Component
       public class ClassB {
           public ClassB() {
               System.out.println("creating instance of ClassB :" + this);
           }
       }

       package com.codeproject.jackie.springdive.beanconfig.yyy;
       import org.springframework.stereotype.Component;

       @Component
       public class ClassC {
           public ClassC() {
               System.out.println("creating instance of ClassC :" + this);
           }
       }

       package com.codeproject.jackie.springdive.beanconfig.xxx;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassB;
       import com.codeproject.jackie.springdive.beanconfig.yyy.ClassC;
       import org.springframework.beans.factory.annotation.Autowired;
       import org.springframework.stereotype.Component;

       @Component
       public class ClassA {
           private ClassB classB;
           private ClassC classC;
           public ClassA() {
               System.out.println("creating instance of ClassA :" + this);
           }
           @Autowired
           public void setClassB(ClassB classB) {
               System.out.println("setting field classB with " + classB + " in ClassA");
               this.classB = classB;
           }
           @Autowired
           public void setClassC(ClassC classC) {
               System.out.println("setting field classC with " + classC + " in ClassA");
               this.classC = classC;
           }
       }
    
而 XML 文件中只保留下面这个:
<context:annotation-config />
         加载 Spring 容器后发现什么都没有输出,没有创建 bean, 也没有注入 bean。为什么呢?
       这是因为 <context:annotation-config /> 仅仅作用于在 Spring 容器中注册过的 bean。由于我删掉了 XML 文件中那三个 bean 的定义所以没有 bean 被创建,<context:annotation-config /> 也就没有了作用目标。
 

四、<context:component-scan>

        Spring 通过 <context:component-scan> 提供了强大的组件扫描功能,在这个 XML 元素中必须指定 base-package 这个属性,其值即为要扫描的包,Spring 会自动扫描指定的包和子包下所有使用了 @Component、@Repository、@Service 以及 @Controller 注解的类,将它们注册为 Spring bean。其中 @Component 注解是基本注解,@Repository、@Service 和 @Controller 分别表示持久层、服务层和表现层中的组件

       下面使用 <context:component-scan> 来简化 XML 配置:

<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig" />
           再次 加载 Spring 容器,  得到正确的输出结果:
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@1fbbd7b2 
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@34d507e9 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@34d507e9 in ClassA 
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@214a7a12 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@214a7a12 in ClassA
           现在将 XML 配置文件修改如下: 
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
           加载 Spring 容器,也可以得到正确的结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc 
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3288df60 
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc in ClassA 
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 in ClassA
           可见,即使 id 为 classA 的这个 bean 不是通过包扫描而是在 XML 文件中手工注册的,注解处理器仍被 <context:component-scan> 应用于所有在 Spring 容器中注册的 bean 上,亦即 <context:component-scan> 有着和 <context:annotation-config /> 相同的功能

       那如果在 XML 配置文件中同时指定了 <context:annotation-config/> 和 <context:component-scan> 呢? 

<context:annotation-config />
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
      没有重复注册 id 为 classA 的这个 bean, 再一次获得了期望的结果:
creating instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3288df60
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@bb273cc in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@45660d6 in ClassA
          这是因为两个标记注册了相同的注解处理器 (如果指定了 < context:component-scan> 那么 < context:annotation-config /> 就会被忽略) ,但是 Spring 只会运行它们一次。

       即使你自己注册注解处理器多次,Spring 将仍然保证只执行一次。修改 XML 配置如下:

<context:annotation-config />
<context:component-scan base-package="com.codeproject.jackie.springdive.beanconfig.yyy" />
<bean id="classA" class="com.codeproject.jackie.springdive.beanconfig.xxx.ClassA" />
<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 instance of ClassB :com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@70d05c13
creating instance of ClassC :com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@42ef83d3
creating instance of ClassA :com.codeproject.jackie.springdive.beanconfig.xxx.ClassA@3801318b
setting field classB with com.codeproject.jackie.springdive.beanconfig.yyy.ClassB@70d05c13 in ClassA
setting field classC with com.codeproject.jackie.springdive.beanconfig.yyy.ClassC@42ef83d3 in ClassA

五、总结

       <context:annotation-config> 是用来激活那些已经在 Spring 容器里注册过的 bean (无论是通过 xml 还是包扫描定义的) 上面的注解,而 <context:component-scan> 除了具有 <context:annotation-config> 的功能之外,还可以扫描 package 来查找并注册 bean。

六、参考资料

         http://stackoverflow.com/questions/7414794/difference-between-contextannotation-config-vs-contextcomponent-scan



关于匹配的通配符是严格的,但是找不到元素'context:component-scan的声明的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于之间的区别、applicationContext.xml中的区别、context:annotation-config 和 context:component-scan等相关知识的信息别忘了在本站进行查找喔。

本文标签: