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

从我读到的内容来看,它们似乎处理不同的注释(@Required, @Autowired等vs @Component, @Repository, @Service等),但从我读到的内容来看,它们注册了相同的bean后处理器类。

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

有人能解释一下这些标签吗?什么是相似的,什么是不同的,是一个被另一个取代,它们互相完善,我需要其中一个吗,还是两个?


当前回答

<context:annotation-config>

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

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

XML: <憨豆先生> @注解:@组件,@服务,@存储库,@控制器 JavaConfig: @Configuration, @Bean

现在有了

<context:component-scan>

它做了两件事:

它扫描带有注释的所有类 @组件、@服务、@存储库、@控制器和@配置并创建一个Bean 它与<context:annotation-config>所做的工作相同。

因此,如果你声明<context:component-scan>,就没有必要再声明<context:annotation-config>了。

Thats所有

例如,一个常见的场景是仅通过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,而存储库对JdbcTemplate、DataSource等组件使用@Autowired

其他回答

Spring允许您做两件事:

自动装配bean 自动发现bean

1. Autowiring Usually in applicationContext.xml you define beans and other beans are wired using constructor or setter methods. You can wire beans using XML or annotations. In case you use annotations, you need to activate annotations and you have to add <context:annotation-config /> in applicationContext.xml. This will simplify the structure of the tag from applicationContext.xml, because you will not have to manually wire beans (constructor or setter). You can use @Autowire annotation and the beans will be wired by type.

避免手动XML配置的一个步骤是

2. Autodiscovery Autodiscovery is simplifying the XML one step further, in the sense that you don't even need too add the <bean> tag in applicationContext.xml. You just mark the specific beans with one of the following annotation and Spring will automatically wire the marked beans and their dependencies into the Spring container. The annotations are as follow: @Controller, @Service, @Component, @Repository. By using <context:component-scan> and pointing the base package, Spring will auto-discover and wire the components into Spring container.


作为结论:

<context:annotation-config />被使用是为了能够使用 @ autowired注解 <context:component-scan />用于确定搜索 特定的bean和自动装配的尝试。

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

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

摘自Spring文档中的基于注释的配置


Spring提供了自动检测“定型”类的功能,并在ApplicationContext中注册相应的BeanDefinitions。

根据org.springframework.stereotype的javadoc:

原型是表示类型或方法在整个体系结构中的角色的注释(在概念级别,而不是实现级别)。 例如:@控制器@服务@存储库等。 它们是供工具和方面使用的(成为切入点的理想目标)。

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

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

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

<context:annotation-config /> 

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

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

启用<context:annotation-config />可以做的所有事情,添加使用原型,例如..@组件,@服务,@存储库。因此,您可以连接整个bean,而不仅仅局限于构造函数或属性!

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

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

我将用一些例子来说明不同点/相同点。

让我们从三个类型为a、B和C的bean的基本设置开始,将B和C注入到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上的bbb和ccc属性:

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。但是我已经在<context:component-scan>中指定了使用包com。所以这完全错过了我的A课,只选了B和C,这是在网站上。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定义,你有注释。

作为最后一个示例,保留带注释的类a、B和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 在应用程序上下文中,即使是手动在XML中注册的A。

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

这是因为两个标记注册了相同的处理工具(如果指定了<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 base-package="package name" />:

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

@组件,@服务,@存储库,@控制器

<上下文:annotation-config / >:

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