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

use*_*097 672 java configuration spring annotations spring-3

我正在学习春天3,我似乎没有把握背后的功能<context:annotation-config><context:component-scan>.

根据我的阅读,他们似乎处理不同的注释(@ Required,@ Autowired etc vs @Component,@ Repository,@ Service等),但也从我读过的内容中注册了相同的bean后处理器类.

为了让我更加困惑,有一个@Required属性@Autowired.

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

小智 1392

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

<context:component-scan>也可以做什么<context:annotation-config><context:component-scan>也扫描包以在应用程序上下文中查找和注册bean.

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

让我们用型的三种豆基本设置开始A,B并且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; 
  }
}
Run Code Online (Sandbox Code Playgroud)

使用以下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>
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

好的,这是预期的输出.但这是春天的"老风格".现在我们有注释,所以让我们使用它们来简化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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
Run Code Online (Sandbox Code Playgroud)

我的XML现在简化为:

<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
Run Code Online (Sandbox Code Playgroud)

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

creating bean B: com.xxx.B@5e5a50
creating bean C: com.xxx.C@54a328
creating bean A: com.yyy.A@a3d4cf
Run Code Online (Sandbox Code Playgroud)

好的,这是错的!发生了什么?为什么我的房产没有自动装配?

嗯,注释是一个很好的功能,但他们自己什么都不做.他们只是注释东西.您需要一个处理工具来查找注释并使用它们执行某些操作.

<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" />
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

好的,这很好,但是我从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;
  }
}
Run Code Online (Sandbox Code Playgroud)

在XML中我们只保留这个:

<context:annotation-config />
Run Code Online (Sandbox Code Playgroud)

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

那是因为,正如我在第一段中所说的那样,<context:annotation-config />唯一适用于在应用程序上下文中注册的bean.因为我删除了三个bean的XML配置,所以没有创建bean并且<context:annotation-config />没有"目标"可以处理.

但这不是一个问题<context:component-scan>,可以扫描一个包以寻找"目标".让我们将XML配置的内容更改为以下条目:

<context:component-scan base-package="com.xxx" />
Run Code Online (Sandbox Code Playgroud)

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

creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff
Run Code Online (Sandbox Code Playgroud)

嗯......有些东西不见了.为什么?

如果您在班closelly看,类A有包com.yyy,但我已经在指定<context:component-scan>使用的软件包com.xxx所以这完全错过了我的A班,只有拿起BC它们的com.xxx软件包.

为了解决这个问题,我还添加了另一个包:

<context:component-scan base-package="com.xxx,com.yyy" />
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

就是这样!现在您不再拥有XML定义,您有注释.

作为最后一个例子,保留带注释的类A,B并将C以下内容添加到XML中,加载上下文后我们会得到什么?

<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

即使A没有通过扫描获得类的bean ,处理工具仍然应用于<context:component-scan>在应用程序上下文中注册的所有bean,即使A是在XML中手动注册的bean .

但是,如果我们有以下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" />
Run Code Online (Sandbox Code Playgroud)

不,没有重复,我们再次得到预期的结果:

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
Run Code Online (Sandbox Code Playgroud)

这是因为两个标签都注册了相同的处理工具(<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" />
Run Code Online (Sandbox Code Playgroud)

仍会产生以下结果:

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
Run Code Online (Sandbox Code Playgroud)

好吧,那就是把它搞砸了.

我希望与@Tomasz Nurkiewicz和@Sean帕特里克·弗洛伊德的响应沿着这条信息,都是你需要了解如何 <context:annotation-config><context:component-scan>工作.

  • 我希望你写完整个Spring手册!有关混淆Spring Framework的任何相关内容的最佳解释.谢谢. (16认同)
  • 引用:"如果指定了<context:component-scan>",则可以省略<context:annotation-config />.为什么要使用annotation-config呢?它为什么存在? (8认同)
  • 如此简单而出色的解释.除了得到答案,我还学会了很好的方式来告诉事情:) (6认同)
  • 很好的答案!没有什么比带有简洁描述的简短清晰示例更好的了。一口气读懂了整件事。 (3认同)
  • 你的写作风格对于初学者来说很容易理解。我希望你能写一本关于基本 Spring 的书。我保证买。 (3认同)

Tom*_*icz 163

我找到了这个很好的摘要,其中哪些注释被哪些声明所取代.通过研究它,你会发现它<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功能.

  • @Tomasz链接下来:( (15认同)

use*_*474 94

Spring允许你做两件事:

  1. 豆类自动装配
  2. 自动发现bean

1.自动装配
通常在applicationContext.xml中定义bean,并使用构造函数或setter方法连接其他bean.您可以使用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的搜索和自动装配的尝试.


Sea*_*oyd 35

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

<context:component-scan> 用于在不使用XML的情况下定义bean

有关详细信息,请阅读:


Spa*_*les 30

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

<context:annotation-config /> 
Run Code Online (Sandbox Code Playgroud)

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

在哪里

<context:component-scan base-package="org.package"/> 
Run Code Online (Sandbox Code Playgroud)

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


Pre*_*raj 29

<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迁移到注释


Sac*_*rma 15

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

Spring 2.5还增加了对JSR-250注释的支持,例如@ Resource,@ PostConstruct和@PreDestroy.使用这些注释也需要在Spring容器中注册某些BeanPostProcessors.与往常一样,这些可以注册为单独的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的代码.


Man*_*dan 12

<context:annotation-config>
Run Code Online (Sandbox Code Playgroud)

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

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

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

现在用

<context:component-scan>
Run Code Online (Sandbox Code Playgroud)

它做了两件事:

  • 它扫描所有使用@ 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" />
Run Code Online (Sandbox Code Playgroud)

我们只是声明了豆子,一无所知<constructor-arg><property>下,二是通过@Autowired配置在自己的班级.这意味着服务使用@Autowired作为其存储库组件,而存储库使用@Autowired作为JdbcTemplate,DataSource等组件.


Lov*_*abu 7

<context:component-scan /> implicitly enables <context:annotation-config/>
Run Code Online (Sandbox Code Playgroud)

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

这意味着不会启用AutowiredAnnotationBeanPostProcessor,并且Spring容器不会处理自动装配注释.


Abd*_*han 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. -->
Run Code Online (Sandbox Code Playgroud)

另一个要注意的重点是context:component-scan隐式调用context:annotation-config以激活bean上的注释.好吧,如果你不想context:component-scan为你隐式激活注释,你可以继续设置context:component-scanto 的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 -->
Run Code Online (Sandbox Code Playgroud)