<上下文:注释配置>和<上下文:组件扫描>

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

从我所读到的内容来看,它们似乎处理不同的注释(,etc vs , ,等等),但从我所读到的内容来看,它们也注册了相同的bean post processor类。@Required@Autowired@Component@Repository@Service

更让我困惑的是,上有一个属性annotation-config<context:component-scan>

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


答案 1

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

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

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

让我们从三个类型为 、 和 的豆子的基本设置开始,并将 这些豆子注入 到 中。ABCBCA

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。

首先,让我们自动连接 bean 上的 和 属性,如下所示:bbbcccA

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的XML配置,所以没有创建Bean,也没有“目标”可以处理。<context:annotation-config /><context:annotation-config />

但这不会是一个问题,因为它可以扫描包以查找要处理的“目标”。让我们将 XML 配置的内容更改为以下条目:<context:component-scan>

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

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

creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff

嗯......缺少某些内容。为什么?

如果你仔细观察这些类,类有包,但我在要使用的包中指定了,所以这完全错过了我的类,只拾取了包和包上的内容。Acom.yyy<context:component-scan>com.xxxABCcom.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 定义,而是有了批注。

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

<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

即使类的 Bean 不是通过扫描获得的,处理工具仍会应用于在应用程序上下文中注册的所有 Bean,即使这些 Bean 是在 XML 中手动注册的。A<context:component-scan>A

但是,如果我们有以下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

这是因为两个标签注册相同的处理工具(如果指定,可以省略),但Spring只负责运行它们一次。<context:annotation-config /><context:component-scan>

即使您自己多次注册处理工具,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

我发现了这个很好的摘要,说明哪些注释被哪些声明拾取。通过研究它,您会发现 它识别由 识别的注释超集,即:<context:component-scan/><context:annotation-config/>

  • @Component, , , ,@Service@Repository@Controller@Endpoint
  • @Configuration, , , , , , , , ,@Bean@Lazy@Scope@Order@Primary@Profile@DependsOn@Import@ImportResource

如您所见,通过CLASSPATH组件扫描和Java@Configuration功能在逻辑上进行了扩展<context:component-scan/><context:annotation-config/>


推荐