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

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

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

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


当前回答

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

其他回答

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

@组件,@服务,@存储库,@控制器,@端点 @Configuration, @Bean, @Lazy, @Scope, @Order, @Primary, @Profile, @DependsOn, @Import, @ImportResource

正如您所看到的,<context:component-scan/>逻辑上扩展了<context:annotation-config/>,具有CLASSPATH组件扫描和Java @Configuration特性。

您可以在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.

Spring允许您做两件事:

自动装配bean 自动发现bean

1. Autowiring Usually in applicationContext.xml you define beans and other beans are wired using constructor or setter methods. You can wire beans using XML or annotations. In case you use annotations, you need to activate annotations and you have to add <context:annotation-config /> in applicationContext.xml. This will simplify the structure of the tag from applicationContext.xml, because you will not have to manually wire beans (constructor or setter). You can use @Autowire annotation and the beans will be wired by type.

避免手动XML配置的一个步骤是

2. Autodiscovery Autodiscovery is simplifying the XML one step further, in the sense that you don't even need too add the <bean> tag in applicationContext.xml. You just mark the specific beans with one of the following annotation and Spring will automatically wire the marked beans and their dependencies into the Spring container. The annotations are as follow: @Controller, @Service, @Component, @Repository. By using <context:component-scan> and pointing the base package, Spring will auto-discover and wire the components into Spring container.


作为结论:

<context:annotation-config />被使用是为了能够使用 @ autowired注解 <context:component-scan />用于确定搜索 特定的bean和自动装配的尝试。

<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>:扫描并激活spring config xml中已注册bean的注释。

<context:component-scan>: Bean注册+ <context:annotation-config>


@Autowired和@Required是目标属性级别,所以bean应该在使用这些注释之前在spring IOC中注册。要启用这些注释,要么必须注册各自的bean,要么包含<context:annotation-config />。例如,<context:annotation-config />仅适用于已注册的bean。

@Required启用RequiredAnnotationBeanPostProcessor处理工具 @Autowired启用AutowiredAnnotationBeanPostProcessor处理工具

注:注释本身没什么事,我们需要一个Processing Tool,它是底层的一个类,负责核心流程。


@Repository, @Service和@Controller是@Component,它们的目标是类级别。

<context:component-scan>它扫描包并找到并注册bean,它包括<context:annotation-config />所做的工作。

将XML迁移到注释