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

时间:2011-09-14 10:28:33

标签: java spring configuration annotations spring-3

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

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

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

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

15 个答案:

答案 0 :(得分:1359)

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

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

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

让我们从三个ABC类型的bean的基本设置开始,BC注入{{1} }}

A

使用以下XML配置:

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; 
  }
}

加载上下文会产生以下输出:

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

好的,这是预期的输出。但这是春天的“老风格”。现在我们有了注释,所以我们可以使用它们来简化XML。

首先,让我们自动对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 上的bbbccc属性进行自动装配:

A

这允许我从XML中删除以下行:

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" />

当我加载上下文时,我得到以下输出:

<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 救援。这将激活它在同一应用程序上下文中定义的bean上找到的注释的操作。

如果我将XML更改为:

<context:annotation-config>

当我加载应用程序上下文时,我得到了正确的结果:

<context:annotation-config />
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />

好的,这很好,但是我从XML中删除了两行并添加了一行。这不是一个很大的区别。带注释的想法是它应该删除XML。

因此,让我们删除XML定义并将其全部替换为注释:

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中我们只保留这个:

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;
  }
}

我们加载上下文,结果是......没什么。没有创建bean,也没有自动装配bean。没事!

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

<context:annotation-config />可以扫描包以寻找“目标”,这不会是一个问题。让我们将XML配置的内容更改为以下条目:

<context:component-scan>

当我加载上下文时,我得到以下输出:

<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>,所以这完全错过了我的{ {1}}课程,仅选取了com.xxx包裹上的AB

要解决这个问题,我还要添加其他包:

C

现在我们得到了预期的结果:

com.xxx

就是这样!现在你不再有XML定义了,你有注释。

作为最后一个示例,保留带注释的类<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 A并将以下内容添加到XML中,加载上下文后我们会得到什么?

B

我们仍然得到了正确的结果:

C

即使没有通过扫描获得类<context:component-scan base-package="com.xxx" /> <bean id="aBean" class="com.yyy.A" /> 的bean,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 仍然会在注册的所有bean上应用处理工具 在应用程序上下文中,即使是在XML中手动注册的A

但是,如果我们有以下XML,我们会得到重复的bean,因为我们已经指定了<context:component-scan>A吗?

<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 可以省略)但是Spring只负责运行它们一次。

即使你自己多次注册处理工具,Spring仍然会确保他们只做一次魔术;这个XML:

<context:annotation-config />

仍会产生以下结果:

<context:component-scan>

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

我希望这些信息以及@Tomasz Nurkiewicz和@Sean Patrick Floyd的回复是你需要了解的 <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 工作。

答案 1 :(得分:160)

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

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

正如您可以通过CLASSPATH组件扫描和Java @Configuration功能看到<context:component-scan/>逻辑扩展 <context:annotation-config/>

答案 2 :(得分:89)

Spring允许你做两件事:

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

    转发手动XML配置的一步是

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


    作为结论:

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

答案 3 :(得分:32)

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

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

有关详细信息,请阅读:

答案 4 :(得分:28)

两者之间的区别非常简单!。

<context:annotation-config /> 

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

在哪里

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

启用<context:annotation-config />可以执行的所有操作,并添加使用构造型,例如.. @Component@Service@Repository。所以你可以连接整个bean而不仅限于构造函数或属性!

答案 5 :(得分:26)

<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处理工具

注意:注释本身无关,我们需要一个处理工具,这是一个下面的类,负责核心流程。

@Repository, @Service and @Controller are @Component ,他们定位课程级别

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

Migrating XML to Annotations

答案 6 :(得分:15)

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

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

取自Annotation Based Configuration

的Spring文档

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

根据org.springframework.stereotype的javadoc:

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

要自动检测此类“刻板印象”类,需要<context:component-scan>标记。

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

答案 7 :(得分:11)

<context:annotation-config>

只有解析@Autowired和@Qualifer注释,这就是依赖注入,还有其他注释做同样的工作,我想怎么样@注入,但所有即将通过注释解决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等组件

答案 8 :(得分:6)

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

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

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

答案 9 :(得分: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-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 -->

答案 10 :(得分:0)

<context:component-scan/>自定义标记除了主要负责扫描java包和从类路径注册bean定义外,还注册了相同的bean定义集。

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

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

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

答案 11 :(得分:0)

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

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

@Component@Service@Repository@Controller

<context:annotation-config />

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

答案 12 :(得分:0)

<i class="fa fa-history" onclick="openWindow()"></i><?php _e("Repost", ET_DOMAIN); ?>

这告诉Spring我将使用Annotated bean作为spring bean,这些将通过<context:annotation-config>注释连接,而不是在spring config xml文件中声明。

@Autowired

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

答案 13 :(得分: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.

答案 14 :(得分:0)

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

spring.io

中也有描述
  

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

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