【问题标题】:Difference between <context:annotation-config> and <context:component-scan><context:annotation-config> 和 <context:component-scan> 之间的区别
【发布时间】:2011-11-16 21:44:17
【问题描述】:

我正在学习 Spring 3,但我似乎没有掌握 &lt;context:annotation-config&gt;&lt;context:component-scan&gt; 背后的功能。

根据我的阅读,它们似乎处理不同的注释@Required@Autowired 等与 @Component@Repository@Service 等),但也来自于我读过他们注册了相同的 bean 后处理器 类。

更让我困惑的是,&lt;context:component-scan&gt; 上有一个annotation-config 属性

有人能解释一下这些标签吗?有什么相似,有什么不同,一个被另一个取代,它们相互补充,我需要它们中的一个,两者都需要吗?

【问题讨论】:

标签: java spring configuration annotations spring-3


【解决方案1】:

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

&lt;context:component-scan&gt; 也可以做 &lt;context:annotation-config&gt; 所做的事情,但 &lt;context:component-scan&gt; 也会扫描包以在应用程序上下文中查找和注册 bean。

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

让我们从ABC 类型的三个bean 的基本设置开始,将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 A 上的 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

好吧,这是错误的!发生了什么?为什么我的属性没有自动装配?

好吧,注释是一个不错的功能,但它们本身并没有任何作用。他们只是注释东西。您需要一个处理工具来查找注释并对其进行处理。

&lt;context:annotation-config&gt; 来救援。这将激活它在定义自身的同一应用程序上下文中定义的 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。没什么!

这是因为,正如我在第一段中所说,&lt;context:annotation-config /&gt; 仅适用于在应用程序上下文中注册的 bean。因为我删除了三个 bean 的 XML 配置,所以没有创建 bean,&lt;context:annotation-config /&gt; 没有“目标”可以处理。

但这对于&lt;context:component-scan&gt; 来说不是问题,它可以扫描一个包以查找“目标”以进行处理。让我们将 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 包,但我已在 &lt;context:component-scan&gt; 中指定使用 com.xxx 包,所以这完全错过了我的 A 课程,只选择了 @ 987654356@ 和 C 位于 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 定义了,你有了注释。

作为最后一个例子,保留注解的类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,处理工具仍然被&lt;context:component-scan&gt;应用到所有注册的bean上 在应用程序上下文中,即使是在 XML 中手动注册的A

但是如果我们有以下 XML,会因为我们同时指定了 &lt;context:annotation-config /&gt;&lt;context:component-scan&gt; 而得到重复的 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

这是因为两个标签都注册了相同的处理工具(如果指定了&lt;context:component-scan&gt;,则可以省略&lt;context:annotation-config /&gt;),但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 的回复是您了解如何 &lt;context:annotation-config&gt;&lt;context:component-scan&gt; 工作。

【讨论】:

  • 引用:“如果指定了 ,则可以省略 ”。那为什么还要使用 annotation-config 呢?为什么会存在?
  • 很好的答案!没有什么比一个简短清晰的例子更简洁的描述了。一口气读懂了整个事情。
  • 我希望你写了整个 Spring 手册!关于与混淆 Spring Framework 相关的任何事情的最佳解释。谢谢。
  • 如此简单而出色的解释。除了得到答案,我还学会了讲事情的好方法:)
  • 你的写作风格对于初学者来说很容易理解。我希望你能写一本关于基本 Spring 的书。我保证会买。
【解决方案2】:
<context:annotation-config>

解析@Autowired@Qualifer注释,仅此而已,关于依赖注入,我认为还有其他注释可以完成相同的工作怎么@Inject,不过都是通过注解来解决DI的。

请注意,即使您声明了 &lt;context:annotation-config&gt; 元素,您必须声明您的类无论如何都是 Bean,请记住我们有三个可用选项

  • XML:&lt;bean&gt;
  • @Annotations:@Component、@Service、@Repository、@Controller
  • JavaConfig:@Configuration,@Bean

现在

<context:component-scan>

它做了两件事:

  • 它扫描所有带有注释的类 @Component、@Service、@Repository、@Controller 和 @Configuration 并创建一个 Bean
  • 它的作用与&lt;context:annotation-config&gt; 的作用相同。

因此,如果您声明&lt;context:component-scan&gt;,则无需再声明&lt;context:annotation-config&gt;

就是这样

一个常见的场景是例如通过 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,没有&lt;constructor-arg&gt;&lt;property&gt;,DI是通过@Autowired在他们自己的类中配置的。这意味着服务将@Autowired 用于其存储库组件,而存储库将@Autowired 用于JdbcTemplate、DataSource 等。组件

【讨论】:

  • 精湛的解释谢谢。 @曼努埃尔乔丹
【解决方案3】:

作为补充,您可以使用@ComponentScan 以注释方式使用&lt;context:component-scan&gt;

spring.io也有描述

配置组件扫描指令以用于 @配置类。提供与 Spring XML 并行的支持 元素。

需要注意的是,如果您使用的是 Spring Boot,则可以通过使用 @SpringBootApplication 注解来隐含 @Configuration 和 @ComponentScan。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      &lt;context:annotation-config&gt; 扫描并激活 spring config xml 中已注册 bean 的注释。

      &lt;context:component-scan&gt;: Bean 注册 + &lt;context:annotation-config&gt;


      @Autowired 和@Required目标属性级别,所以在使用这些注解之前,bean 应该在 Spring IOC 中注册。要启用这些注释,要么必须注册相应的 bean,要么包括 &lt;context:annotation-config /&gt;。即&lt;context:annotation-config /&gt; 仅适用于已注册的 bean。

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

      注意:注解本身无关,我们需要一个Processing Tool,它是下面的一个类,负责核心流程。


      @Repository, @Service and @Controller are @Component,他们针对班级级别

      &lt;context:component-scan&gt; 扫描包并查找并注册 bean,其中包含 &lt;context:annotation-config /&gt; 所做的工作。

      Migrating XML to Annotations

      【讨论】:

        【解决方案6】:

        &lt;context:annotation-config&gt;:

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

        &lt;context:component-scan base-package="com.test..."&gt;

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

        【讨论】:

          【解决方案7】:

          &lt;context:component-scan base-package="package name" /&gt;:

          这用于告诉容器我的包中有 bean 类扫描那些 bean 类。为了通过 bean 顶部的容器扫描 bean 类,我们必须编写如下立体类型注释之一。

          @Component@Service@Repository@Controller

          &lt;context:annotation-config /&gt;:

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

          【讨论】:

            【解决方案8】:
            <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 -->
            

            【讨论】:

              【解决方案9】:
              <context:component-scan /> implicitly enables <context:annotation-config/>
              

              尝试使用 &lt;context:component-scan base-package="..." annotation-config="false"/&gt; ,在您的配置中 @Service, @Repository, @Component 工作正常,但 @Autowired,@Resource@Inject 不起作用。

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

              【讨论】:

              • 这个帮助我理解了 隐式启用 ;也就是说,它会扫描 bean 定义以及是否需要注入。我尝试了 annotation-config="false",除非我使用 明确设置,否则注入不起作用。终于我的理解比以前好多了!
              【解决方案10】:

              两者的区别真的很简单!

              <context:annotation-config /> 
              

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

              在哪里

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

              启用&lt;context:annotation-config /&gt; 可以做的所有事情,并添加使用刻板印象,例如.. @Component@Service@Repository。因此,您可以连接整个 bean,而不仅限于构造函数或属性!

              【讨论】:

                【解决方案11】:

                Spring 允许你做两件事:

                1. bean 的自动装配
                2. bean 的自动发现

                1.自动装配
                通常在 applicationContext.xml 中定义 bean 和其他 bean 使用 构造函数或设置方法。您可以使用 XML 或注释连接 bean。 如果您使用注释,则需要激活注释并且必须添加 &lt;context:annotation-config /&gt;applicationContext.xml 中。这将简化 applicationContext.xml 中的标签结构,因为您不必手动连接 bean(构造函数或设置器)。您可以使用@Autowire 注释,bean 将按类型连接。

                转义手动 XML 配置的一个步骤是

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


                作为结论:

                • 使用&lt;context:annotation-config /&gt; 是为了能够使用 @Autowired 注释
                • &lt;context:component-scan /&gt; 用于确定搜索 特定的 bean 和自动装配的尝试。

                【讨论】:

                • 是否可以使用组件扫描而不是注释配置?
                • 在上下文中使用 annotation-config="false":annotation-config 标签。
                【解决方案12】:

                &lt;context:annotation-config&gt; 标签告诉 Spring 扫描代码库以自动解析包含 @Autowired 注解的类的依赖要求。

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

                取自 Annotation Based Configuration 的 Spring 文档


                Spring 提供了自动检测“原型”类并使用 ApplicationContext 注册相应 BeanDefinition 的能力。

                根据org.springframework.stereotype的javadoc:

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

                要自动检测此类“刻板印象”类,需要&lt;context:component-scan&gt; 标签。

                &lt;context:component-scan&gt; 标记还告诉 Spring 扫描代码以查找指定包(及其所有子包)下的可注入 bean。

                【讨论】:

                  【解决方案13】:

                  &lt;context:component-scan/&gt; 自定义标记注册与 相同的一组 bean 定义,除了它的主要职责是扫描 java 包和从类路径注册 bean 定义。

                  如果出于某种原因要避免这种默认 bean 定义的注册,这样做的方法是在组件扫描中指定一个额外的“annotation-config”属性,这样:

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

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

                  【讨论】:

                    【解决方案14】:

                    &lt;context:annotation-config&gt; 在 bean 中激活许多不同的注解,无论它们是在 XML 中定义的还是通过组件扫描定义的。

                    &lt;context:component-scan&gt; 用于在不使用 XML 的情况下定义 bean

                    更多信息,请阅读:

                    【讨论】:

                    • 您能进一步解释一下吗?如果我使用&lt;context:component-scan&gt;,我将无法使用 XML 覆盖 bean 定义?
                    • @user938214097 您可以在 XML 中或通过组件扫描的注释定义 bean
                    • 使用&lt;context:component-scan&gt;够吗?如果我不使用&lt;context:annotation-config&gt;,我会丢失一些东西吗?
                    • @Tomasz 似乎已经回答了这个问题
                    【解决方案15】:

                    我发现了这个不错的summary,其中的注释被哪些声明拾取。通过研究你会发现&lt;context:component-scan/&gt;识别了&lt;context:annotation-config/&gt;识别的注解的超集,即:

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

                    如您所见,&lt;context:component-scan/&gt; 在逻辑上扩展 &lt;context:annotation-config/&gt; 具有 CLASSPATH 组件扫描和 Java @Configuration 功能。

                    【讨论】:

                      猜你喜欢
                      • 2012-11-19
                      • 1970-01-01
                      • 2011-04-28
                      • 2011-10-04
                      • 1970-01-01
                      • 2014-04-11
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多