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

我正在学习Spring 3,而我似乎并没有把握<context:annotation-config><context:component-scan>背后的function。

从我读过的,他们似乎处理不同的注释(@Required,@Autowired等与@Component,@Repository,@Service等),但也从我读了他们注册相同的豆后处理器类。

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

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

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

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

我将用一些例子来说明不同之处/相似之处。

让我们从ABC三种豆类的基本设置开始,将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; } } 

使用以下XMLconfiguration:

 <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 

好的,这是预期的输出。 但这是“旧式”的spring。 现在我们有注释,所以可以使用这些来简化XML。

首先,让autowire bean上的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 

好的,这是错误的! 发生了什么? 为什么不是我的属性自动assembly?

那么,注释是一个很好的function,但它们本身什么都不做。 他们只是注释的东西。 您需要一个处理工具来查找注释并与他们做一些事情。

<context:annotation-config>来拯救。 这激活了它在自己定义的同一个应用程序上下文中定义的bean上find的注释的动作。

如果我将我的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定义,并用注释replace它们:

 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被创build,没有bean被自动assembly。 没有!

这是因为,正如我在第一段中所说的, <context:annotation-config />只适用于在应用程序上下文中注册的bean。 因为我删除了三个bean的XMLconfiguration,所以没有创buildbean,并且<context:annotation-config />没有“目标”。

但是对于<context:component-scan>这不会是一个问题,它可以扫描一个打包的“目标”进行工作。 我们将XMLconfiguration的内容更改为以下条目:

 <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>指定了使用包com.xxx所以这完全错过了我的A类,只拾取了BCcom.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,处理工具仍然通过<context:component-scan>应用程序上下文中注册的所有bean应用,即使是在XML中手动注册的A也是如此。

但是如果我们有下面的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: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/>识别的注释的超集,即:

  • @Repository @Endpoint@Repository @Controller@Repository@Controller @Endpoint@Controller @Endpoint
  • @Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @DependsOn@Import @ImportResource

正如您可以看到<context:component-scan/> <context:annotation-config/>使用CLASSPATH组件扫描和Java @Configurationfunction从逻辑上扩展了 <context:annotation-config/>

Spring允许你做两件事:

  1. 豆类自动assembly
  2. 豆的自动发现

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

转义手动XMLconfiguration的一步是

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


作为结论:

  • 为了能够使用@Autowired注解,使用<context:annotation-config />
  • <context:component-scan />用于确定search特定的bean并尝试自动assembly。

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

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

欲了解更多信息,请阅读:

  • 3.9。 基于注解的容器configuration
  • 3.10。 类path扫描和托pipe组件

两者的区别真的很简单!

 <context:annotation-config /> 

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

在哪里

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

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

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

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

Spring 基础注释configuration文档


Spring提供了自动检测'stereotyped'类的能力,并在ApplicationContext中注册了相应的BeanDefinitions。

根据org.springframework.stereotype的 javadoc:

定型是注释,表示types或方法在整个架构中(在概念层面而不是实施层面)的angular色。 示例:@Controller @Service @Repository等。这些是供工具和方面使用的(作为切入点的理想目标)。

为了自动检测这样的“原型”类,需要<context:component-scan>标签。

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

TL;博士
<context:annotation-config> 在spring config xml中扫描和激活已经注册的bean的注释。

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


@Autowired和@Requiredtarget属性级别,所以bean在使用这些注释之前应该在spring IOC中注册。 要启用这些注释,必须注册相应的bean或者包含<context:annotation-config /> 。 即<context:annotation-config />只能用于注册的bean。

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

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


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

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

 <context:annotation-config> 

parsing@Autowired和@Qualifer注解,这就是所有关于dependency injection ,还有其他注释做同样的工作,我想怎么@Inject,但所有通过注释来解决DI。

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

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

现在用

 <context:component-scan> 

它有两件事情:

  • 它扫描所有用@Component,@Service,@Repository,@Controller和@Configuration注解的类并创build一个Bean
  • 它在<context:annotation-config>执行相同的工作。

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

就这样

例如,常见的情况是通过XML声明一个bean,然后通过注释parsingDI

 <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在自己的类中configuration的。 这意味着服务使用@Autowired来存储它们的Repositories组件,而Repositories使用@Autowired作为JdbcTemplate,DataSource等.components

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

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

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

 <context:annotation-config/> <!-- is used to activate the annotation for beans --> <context:component-scan base-package="xyMyClass" /> <!-- 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元素context:component-scanfalse

总结:

 <context:annotation-config/> <!-- activates the annotations --> <context:component-scan base-package="xyMyClass" /> <!-- activates the annotations + register the beans by looking inside the base-package --> 

一个<context:component-scan/>定制标记除了负责扫描java包和从类path注册bean定义之外,还注册了一组相同的bean定义。

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

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

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

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

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

@Component @Service@Repository @Controller@Repository@Controller

<context:annotation-config />

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

背景:注解configuration:

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

上下文:component-scan base-package =“com.test …”:这告诉Spring容器,从哪里开始search那些带注释的bean。 在这里spring将search所有的分包