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

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

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

有人能解释一下这些标签吗?什么是相似的,什么是不同的,是一个被另一个取代,它们互相完善,我需要其中一个吗,还是两个?


当前回答

<context:annotation-config>标记告诉Spring扫描代码库,以自动解决包含@Autowired注释的类的依赖关系需求。

Spring 2.5还增加了对JSR-250注释的支持,比如@Resource、@PostConstruct和@PreDestroy。使用这些注释还要求在Spring容器中注册某些beanpostprocessor。与往常一样,这些可以注册为单独的bean定义,但也可以通过在spring配置中包含<context:annotation-config>标记来隐式注册。

摘自Spring文档中的基于注释的配置


Spring提供了自动检测“定型”类的功能,并在ApplicationContext中注册相应的BeanDefinitions。

根据org.springframework.stereotype的javadoc:

原型是表示类型或方法在整个体系结构中的角色的注释(在概念级别,而不是实现级别)。 例如:@控制器@服务@存储库等。 它们是供工具和方面使用的(成为切入点的理想目标)。

要自动检测这样的“原型”类,需要<context:component-scan>标记。

<context:component-scan>标记还告诉Spring在指定的包(及其所有子包)下扫描可注入bean的代码。

其他回答

<context:component-scan /> implicitly enables <context:annotation-config/>

try with <context:component-scan base-package="…" annotation-config="false"/>,在你的配置中@Service, @Repository, @Component工作正常,但是@Autowired,@Resource和@Inject不工作。

这意味着AutowiredAnnotationBeanPostProcessor将不会被启用,Spring容器将不会处理自动装配注释。

<context:annotation-config>

只解决@Autowired和@Qualifer注释,这就是所有的,它关于依赖注入,还有其他注释做同样的工作,我认为如何@Inject,但都是关于通过注释解决DI。

注意,即使声明了<context:annotation-config>元素,也必须声明类是Bean,记住我们有三个可用选项

XML: <憨豆先生> @注解:@组件,@服务,@存储库,@控制器 JavaConfig: @Configuration, @Bean

现在有了

<context:component-scan>

它做了两件事:

它扫描带有注释的所有类 @组件、@服务、@存储库、@控制器和@配置并创建一个Bean 它与<context:annotation-config>所做的工作相同。

因此,如果你声明<context:component-scan>,就没有必要再声明<context:annotation-config>了。

Thats所有

例如,一个常见的场景是仅通过XML声明bean,并通过注释解析DI

<bean id="serviceBeanA" class="com.something.CarServiceImpl" />
<bean id="serviceBeanB" class="com.something.PersonServiceImpl" />
<bean id="repositoryBeanA" class="com.something.CarRepository" />
<bean id="repositoryBeanB" class="com.something.PersonRepository" />

我们只声明了bean,没有关于<constructor-arg>和<property>, DI是通过@Autowired在它们自己的类中配置的。这意味着服务对它们的存储库组件使用@Autowired,而存储库对JdbcTemplate、DataSource等组件使用@Autowired

<context:annotation-config/> <!-- is used to activate the annotation for beans -->
<context:component-scan base-package="x.y.MyClass" /> <!-- is for the Spring IOC container to look for the beans in the base package. -->

要注意的另一个重要点是context:component-scan隐式调用context:annotation-config来激活bean上的注释。如果你不想让context:component-scan隐式地为你激活注释,你可以继续将context:component-scan的annotation-config元素设置为false。

总结:

<context:annotation-config/> <!-- activates the annotations --> 
<context:component-scan base-package="x.y.MyClass" /> <!-- activates the annotations + register the beans by looking inside the base-package -->

<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。

首先,让我们像这样自动装配bean A上的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中删除以下行:

<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。但是我已经在<context:component-scan>中指定了使用包com。所以这完全错过了我的A课,只选了B和C,这是在网站上。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定义,你有注释。

作为最后一个示例,保留带注释的类a、B和C,并将以下内容添加到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不是通过扫描获得的,处理工具仍然通过<context:component-scan>应用于所有注册的bean 在应用程序上下文中,即使是手动在XML中注册的A。

但是如果我们有下面的XML,我们会因为同时指定了<context:annotation-config />和<context:component-scan>而得到重复的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: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>工作。

您可以在spring上下文模式文件中找到更多信息。 以下内容来自spring-context-4.3.xsd

<conxtext:annotation-config />
Activates various annotations to be detected in bean classes: Spring's @Required and
@Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),
JAX-WS's @WebServiceRef (if available), EJB 3's @EJB (if available), and JPA's
@PersistenceContext and @PersistenceUnit (if available). Alternatively, you may
choose to activate the individual BeanPostProcessors for those annotations.

Note: This tag does not activate processing of Spring's @Transactional or EJB 3's
@TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
tag for that purpose.
<context:component-scan>
Scans the classpath for annotated components that will be auto-registered as
Spring beans. By default, the Spring-provided @Component, @Repository, @Service, @Controller, @RestController, @ControllerAdvice, and @Configuration stereotypes    will be detected.

Note: This tag implies the effects of the 'annotation-config' tag, activating @Required,
@Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit
annotations in the component classes, which is usually desired for autodetected components
(without external configuration). Turn off the 'annotation-config' attribute to deactivate
this default behavior, for example in order to use custom BeanPostProcessor definitions
for handling those annotations.

Note: You may use placeholders in package paths, but only resolved against system
properties (analogous to resource paths). A component scan results in new bean definitions
being registered; Spring's PropertySourcesPlaceholderConfigurer will apply to those bean
definitions just like to regular bean definitions, but it won't apply to the component
scan settings themselves.