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


690

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

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

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

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



总结:component-scan尽可能使用 。
杰瑞·钦

Answers:


1419

<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" 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的bbbccc属性,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" 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>use程序包中指定了该程序包,com.xxx因此这完全错过了我的A课程,只进行了学习,BC这些内容都放在了com.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定义,而有了注释。

作为最后一个示例,保留带注释的类AB并将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,即使对于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:annotation-config />如果<context:component-scan>已指定,则可以省略),但是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帕特里克·弗洛伊德的响应沿着这条信息,都是你需要了解如何 <context:annotation-config><context:component-scan>工作。


8
Quote:“如果指定了<context:component-scan>,则可以省略<context:annotation-config />”。那么为什么要使用注释配置?为什么存在?
CodeClimber

2
好答案!简明扼要的说明无异于简短示例。一读就能理解整个过程。
Jigish 2013年

19
希望您写了整个Spring手册!关于与Spring Framework混淆相关的任何事情的最佳解释。谢谢。
eskalera

7
如此简单而出色的解释。除了得到答案之外,我还学到了很好的讲述方式的方法:)
Amir Al

2
您的写作风格对于初学者来说很容易理解。希望您能写一本有关基本Spring的书。我保证买。
emeraldhieu 2014年

167

我发现了关于哪些注释是由哪些声明选择的一个很好的摘要。通过研究,您将发现<context:component-scan/>识别由识别的注释的超集<context:annotation-config/>,即:

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

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


16
@Tomasz链接关闭:(
Anand Rockzz

95

Spring使您可以做两件事:

  1. 自动接线
  2. 自动发现豆

1.自动装配
通常,您在applicationContext.xml中定义bean,而其他bean则使用构造函数或setter方法进行装配。您可以使用XML或注释对bean进行接线。在您使用注释的情况下,你需要激活注解,你必须添加 <context:annotation-config />applicationContext.xml中。这将简化applicationContext.xml中标记的结构,因为您不必手动连接bean(构造函数或setter)。您可以使用@Autowire注释,并且将按类型连接bean。

转义手动XML配置的步骤是

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


作为结论:

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

1
是否可以以某种方式使用组件扫描而不是注释配置?
Koray Tugay,2015年

在context:注解配置标记中使用注解配置=“ false”。
萨拉(Sara)

38

<context:annotation-config> 激活bean中的许多不同注释,无论它们是用XML定义还是通过组件扫描定义的。

<context:component-scan> 用于不使用XML定义bean

有关更多信息,请阅读:


您能进一步解释一下吗?如果使用,<context:component-scan>我将无法使用XML覆盖bean定义吗?
user938214097 2011年

@ user938214097,您可以使用XML或通过带有组件扫描的注释来定义bean
Sean Patrick Floyd

使用够了<context:component-scan>吗?如果不使用,我会丢东西<context:annotation-config>吗?
user938214097

@Tomasz似乎已经回答了
肖恩·帕特里克·弗洛伊德

31

两者之间的区别真的很简单!

<context:annotation-config /> 

使您能够使用仅限于仅连接bean!的属性和构造函数的注释。

在哪里

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

启用一切<context:annotation-config />可以做的,与另外使用定型例如.. @Component@Service@Repository。因此,您可以连接整个bean,而不仅限于构造函数或属性!


31

<context:annotation-config>扫描并激活spring config xml中已经注册的bean的注释。

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


@Autowired和@Required目标属性级别,因此bean在使用这些注释之前应在spring IOC中注册。要启用这些注释,必须注册各自的bean或include <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迁移到注释


15

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

Spring 2.5还添加了对JSR-250批注的支持,例如@ Resource,@ PostConstruct和@PreDestroy。使用这些批注还需要在Spring容器中注册某些BeanPostProcessor。与往常一样,这些可以注册为单独的bean定义,但是也可以通过包含以下内容来隐式注册它们:<context:annotation-config>在spring配置中 tag。

摘自基于注释的 Spring文档


Spring提供了自动检测“定型”类并向ApplicationContext注册相应BeanDefinition的功能。

根据org.springframework.stereotype的 javadoc

刻板印象是表示类型或方法在整个体系结构中的作用的注释(在概念上而不是在实现上)。示例:@Controller @Service @Repository等。这些对象供工具和方面使用(使切入点成为理想的目标)。

要自动检测此类“定型”类,<context:component-scan>需要使用标签。

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


14
<context:annotation-config>

解析@Autowired@Qualifer批注,此而已,这就是关于Dependency Injection的问题,还有其他批注执行相同的工作,我想@Inject,但全部都是通过批注解决DI的。

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

  • XML: <bean>
  • @Annotations:@ Component,@ Service,@ Repository,@ Controller
  • JavaConfig:@ Configuration,@ Bean

现在用

<context:component-scan>

它有两件事:

  • 它扫描所有以@ Component,@ Service,@ Repository,@ Controller和@Configuration注释的类,并创建一个Bean。
  • 它做同样的工作<context:annotation-config>

因此,如果您声明<context:component-scan>,也不再需要声明<context:annotation-config>

就这样

常见的情况是,例如,通过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用于其存储库组件,而存储库将@Autowired用于JdbcTemplate,DataSource等。


1
精湛的解释谢谢。@Manuel Jordan
BALS

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

尝试<context:component-scan base-package="..." annotation-config="false"/>在配置中使用@ Service,@ Repository,@ Component正常,但是@ Autowired,@ Resource@Inject不起作用。

这意味着将不会启用AutowiredAnnotationBeanPostProcessor,并且Spring容器将不会处理Autowiring注释。


这帮助我理解<context:component-scan />隐式启用<context:annotation-config />; 它扫描Bean定义以及所需的注入。我尝试使用注解-config =“ false”,除非我使用<context:annotation-config />进行了显式设置,否则注入不会起作用。最后我的理解比以前更好了!
CuriousMind '16

5
<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-scanto 的注释配置元素设置为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 -->

1

<context:component-scan base-package="package name" />

这是用来告诉容器我的程序包中有bean类,请扫描这些bean类。为了按容器在Bean顶部扫描Bean类,我们必须编写一个立体声类型注释,如下所示。

@Component@Service@Repository@Controller

<context:annotation-config />

如果我们不想用XML显式地编写bean标签,那么容器如何知道bean中是否存在自动接线。这可以通过使用@Autowired注释来实现。我们必须通知容器,我的bean中有自动接线context:annotation-config


0

一个<context:component-scan/>自定义标签注册同一组的bean定义为通过将从类路径扫描Java包和注册bean定义的首要责任做,分开。

如果出于某种原因要避免默认bean定义的注册,则该方法是在component-scan中指定一个附加的“ annotation-config”属性,方法是:

<context:component-scan basePackages="" annotation-config="false"/>

参考:http : //www.java-allandsundry.com/2012/12/contextcomponent-scan-contextannotation.html


0

<context:annotation-config>

这告诉Spring,我将使用带注释的bean作为Spring bean,并且将通过@Autowired注释进行连线,而不是在spring config xml文件中声明。

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

这告诉Spring容器,从哪里开始搜索那些带注释的bean。在这里spring将搜索基本包的所有子包。


0

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

0

作为补充,您可以使用@ComponentScan<context:component-scan>批注的方式使用。

它也描述在 spring.io

配置组件扫描指令以与@Configuration类一起使用。提供与Spring XML元素平行的支持。

需要注意的一件事是,如果您使用的是Spring Boot,则可以使用@SpringBootApplication批注隐含@Configuration和@ComponentScan。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.