我正在学习Spring 3,但我似乎没有掌握<context:annotation-config>
和<context:component-scan>
背后的功能。
从我读到的内容来看,它们似乎处理不同的注释(@Required
、@Autowired
等与@Component
、@Repository
、@Service
等(,但从我读到的内容来看,它们注册了相同的 bean 后处理器类。
更让我困惑的是,<context:component-scan>
上有一个annotation-config
属性.
有人可以阐明这些标签吗?有什么相似,有什么不同,是一个被另一个取代,它们相互完善,我需要其中一个,两者都需要吗?
<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.yyy
但我在<context:component-scan>
中指定使用包com.xxx
所以这完全错过了我的A
类,只拿起了com.xxx
包上的B
和C
。
为了解决这个问题,我还添加了这个其他包:
<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,我们会得到重复的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/>
识别的注释的超集,即:
-
@Component
、@Service
、@Repository
、@Controller
、@Endpoint
-
@Configuration
,@Bean
,@Lazy
,@Scope
,@Order
,@Primary
,@Profile
,@DependsOn
,@Import
,@ImportResource
如您所见<context:component-scan/>
通过 CLASSPATH 组件扫描和 Java @Configuration功能在逻辑上扩展了<context:annotation-config/>
。
Spring 允许你做两件事:
- 豆子的自动接线
- 自动发现 bean
1. 自动接线
通常在 applicationContext 中.xml您定义 bean,而其他 bean 使用构造函数或 setter 方法。您可以使用 XML 或注释来连接 bean。如果您使用注释,则需要激活注释并且必须添加 <context:annotation-config />
在应用程序上下文中.xml。这将简化来自 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 />
用于确定搜索特定的豆子和自动接线的尝试。
<context:annotation-config>
激活 bean 中的许多不同的注释,无论它们是在 XML 中定义的还是通过组件扫描定义的。
<context:component-scan>
用于在不使用 XML 的情况下定义 bean
欲了解更多信息,请阅读:
- 3.9. 基于注释的容器配置
- 3.10. 类路径扫描和托管组件
<context:annotation-config>
: 扫描并激活 Spring config xml 中已注册的 bean 的注释。
<context:component-scan>
: 豆注册 + <context:annotation-config>
@Autowired和@Required是目标属性级别,因此 Bean 应在使用这些注释之前在春季 IOC 中注册。要启用这些注释,必须注册相应的 bean 或包含<context:annotation-config />
。即 <context:annotation-config />
仅适用于注册的豆类。
@Required启用RequiredAnnotationBeanPostProcessor
处理工具
@Autowired启用AutowiredAnnotationBeanPostProcessor
处理工具
注意:注解本身无关紧要,我们需要一个处理工具,它是下面的一个类,负责核心流程。
@Repository、@Service和@Controller@Component,它们针对班级水平。
<context:component-scan>
,它会扫描包并查找和注册 bean,它包括 <context:annotation-config />
完成的工作。
将 XML 迁移到批注
两者之间的区别非常简单!
<context:annotation-config />
使您能够使用仅限于连接 bean 的属性和构造函数的注释!
其中作为
<context:component-scan base-package="org.package"/>
启用<context:annotation-config />
可以执行的所有操作,此外还使用刻板印象,例如。 @Component
、@Service
、@Repository
。因此,您可以连接整个 bean,而不仅仅是局限于构造函数或属性!
<context:annotation-config>
只解决@Autowired
和@Qualifer
注释,仅此而已,它是关于依赖注入的,还有其他注释做同样的工作,我认为多么@Inject
,但都是要通过注释解决 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,存储库对 JdbcTemplate、数据源等使用@Autowired。组件
<context:annotation-config>
标签告诉 Spring 扫描代码库,以自动解决包含@Autowired注释的类的依赖需求。
2.5 还增加了对 JSR-250 注释的支持,例如 @Resource、@PostConstruct 和 @PreDestroy.使用这些注释还需要在 Spring 容器中注册某些 BeanPostProcessor。与往常一样,这些可以注册为单独的 bean 定义,但它们也可以通过在 spring 配置中包含 <context:annotation-config>
标记来隐式注册。
取自基于注释的配置的 Spring 文档
Spring 提供了自动检测"构造型"类并将相应的 BeanDefinition 注册到 ApplicationContext 的功能。
根据org.springframework.stereotype的javadoc
:构造型是表示类型或方法在整个体系结构中的角色的注释(在概念级别,而不是实现级别(。示例:@Controller @Service @Repository等。这些旨在供工具和方面使用(成为切入点的理想目标(。
要自动检测此类"构造型"类,需要<context:component-scan>
标记。
<context:component-scan>
标签还告诉 Spring 扫描指定包装(及其所有子包装(下的可注射豆的代码。
<context:component-scan /> implicitly enables <context:annotation-config/>
尝试使用 <context:component-scan base-package="..." annotation-config="false"/>
,在您的配置@Service、@Repository 中,@Component工作正常,但@Autowired,@Resource和@Inject不起作用。
这意味着AutowiredAnnotationBeanPostProcessor将不会启用,Spring 容器将不会处理Autowire注释。
<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
的注释配置元素设置为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 -->
<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
通知容器我的豆子中有自动布线。
<context:component-scan/>
定制标记除了主要负责扫描 Java 包和从类路径注册 Bean 定义之外,还注册与 相同的一组 Bean 定义。
如果由于某种原因要避免这种缺省 Bean 定义的注册,那么这样做的方法是在组件扫描中指定一个额外的"注释配置"属性,这样:
<context:component-scan basePackages="" annotation-config="false"/>
参考:http://www.java-allandsundry.com/2012/12/contextcomponent-scan-contextannotation.html
> <context:annotation-config>
:
这告诉 Spring 我将使用带注释的 bean 作为春豆,这些豆子将通过@Autowired
注解进行连接,而不是在 spring config xml 文件中声明。
<context:component-scan base-package="com.test...">
:
这告诉 Spring 容器,从哪里开始搜索那些带注释的 bean。在这里,弹簧将搜索基本包的所有子包。
Spring 上下文模式文件中找到更多信息。以下是在春季上下文-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.
作为补充,您可以使用@ComponentScan
以注释方式使用<context:component-scan>
。
它也在 spring.io
配置组件扫描指令以用于 @Configuration类。提供与Spring XML并行的支持 元素。
需要注意的一点是,如果您使用的是 Spring Boot,则可以通过使用@SpringBootApplication注释来暗示@Configuration和@ComponentScan。