问题

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

根据我的阅读,他们似乎处理不同的注释(@ Required,@ Autowired等vs @Component,@ Repository,@ Service等),但是从我读过的内容中他们注册了相同的bean后处理器类。

为了让我更加困惑,在<context:component-scan>上有一个annotation-config属性。

有人可以对这些标签有所了解吗?什么是相似的,什么是不同的,一个被另一个取代,它们相互完成,我是否需要其中一个,两者兼而有之?


#1 热门回答(1281 赞)

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

 <context:component-scan>也可以做什么<context:annotation-config>但是<context:component-scan>还会扫描包以在应用程序上下文中查找和注册bean。

我将用一些例子来说明差异/相似之处。

让我们从三个类型为“A”,“B”和“C”的bean的基本设置开始,将“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; 
  }
}

使用以下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。

首先,让我们在beanA上自动装配bbbccc属性,如下所示:

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

嗯......有些东西不见了。为什么?

如果你看看类的关闭,类'A有包com.yyy但是我在<context:component-scan>中指定使用包com.xxx所以这完全错过了我的A class并且只获取com.xxx包中的BC`。

为了解决这个问题,我还添加了另一个包:

<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定义,你有注释。

作为最后一个例子,保留带注释的类ABC并将以下内容添加到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,处理工具仍然被应用程序上下文中注册的所有bean上的<context:component-scan>应用,即使对于手动注册的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" />

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

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:component-scan>,``context:annotation-config />`可以省略)但是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

好吧,那就是把它搞砸了。

我希望这些信息以及@Tomasz Nurkiewicz和@Sean Patrick Floyd的回复都是你需要了解<context:annotation-config><context:component-scan>的工作原理。


#2 热门回答(155 赞)

我找到了这个很好的889191989,其中的注释被哪些声明所取代。通过研究它,你会发现<context:component-scan />识别<context:annotation-config />识别的注释的超集,即:

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

正如你所看到的那样``context:component-scan />logically**使用CLASSPATH组件扫描和Java @Configuration功能扩展了** <context:annotation-config />`。


#3 热门回答(81 赞)

Spring允许你做两件事:

  • 豆类自动装配
  • 自动发现bean

1.自动装配
通常inapplicationContext.xmlyou使用构造函数或setter方法定义bean和其他bean。你可以使用XML或注释来连接bean。如果你使用注释,则需要激活注释,并且必须添加<context:annotation-config />inapplicationContext.xml。这将简化标签fromapplicationContext.xml的结构,因为你不必手动连接bean(构造函数或setter)。你可以使用@Autowire注释,bean将按类型连接。

转义手动XML配置的一步是

2.自动发现
自动发现进一步简化了XML,因为你甚至不需要在inapplicationContext.xml中添加<bean>标记。你只需使用以下注释之一标记特定bean,Spring将自动将标记的bean及其依赖项连接到Spring容器中。注释如下:@ Controller,@ Service,@ Component,@ Repository。通过使用<context:component-scan>并指向基础包,Spring将自动发现并将组件连接到Spring容器中。

作为结论:

  • <context:annotation-config />用于使用@Autowired注释
  • <context:component-scan />用于确定特定bean的搜索和自动装配的尝试。

原文链接