context:component-scan使用说明

https://blog.csdn.net/chunqiuwei/article/details/16115135

默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,如@Component(组件),@Service(服务),@Controller(控制器),@Repository(数据仓库)。

我们具体看下<context:component-scan>的一些属性,以下是一个比较具体的<context:component-scan>配置

复制代码


 
 
  1. <context:component-scan base-package="com.wjx.betalot" <!-- 扫描的基本包路径 -->
  2. annotation-config="true" <!-- 是否激活属性注入注解 -->
  3. name-generator="org.springframework.context.annotation.AnnotationBeanNameGenerator" <!-- Bean的ID策略生成器 -->
  4. resource-pattern="**/*.class" <!-- 对资源进行筛选的正则表达式,这边是个大的范畴,具体细分在include-filter与exclude-filter中进行 -->
  5. scope-resolver="org.springframework.context.annotation.AnnotationScopeMetadataResolver" <!-- scope解析器 ,与scoped-proxy只能同时配置一个 -->
  6. scoped-proxy="no" <!-- scope代理,与scope-resolver只能同时配置一个 -->
  7. use-default-filters="false" <!-- 是否使用默认的过滤器,默认值true -->
  8. >
  9. <!-- 注意:若使用include-filter去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖 -->
  10. <!-- annotation是对注解进行扫描 -->
  11. <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
  12. <!-- assignable是对类或接口进行扫描 -->
  13. <context:include-filter type="assignable" expression="com.wjx.betalot.performer.Performer"/>
  14. <context:include-filter type="assignable" expression="com.wjx.betalot.performer.impl.Sonnet"/>
  15. <!-- 注意:在use-default-filters="false"的情况下,exclude-filter是针对include-filter里的内容进行排除 -->
  16. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
  17. <context:exclude-filter type="assignable" expression="com.wjx.betalot.performer.impl.RainPoem"/>
  18. <context:exclude-filter type="regex" expression=".service.*"/>
  19. </context:component-scan>

复制代码

以上配置注释已经很详细了,当然因为这些注释,你若是想复制去验证,你得删掉注释。我们具体再说明一下这些注释:

back-package:标识了<context:component-scan>元素所扫描的包,可以使用一些通配符进行配置

annotation-config:<context:component-scan>元素也完成了<context:annotation-config>元素的工作,开关就是这个属性,false则关闭属性注入注解功能

name-generator:这个属性指定你的构造型注解,注册为Bean的ID生成策略,这个生成器基于接口BeanNameGenerator实现generateBeanName方法,你可以自己写个类去自定义策略。这边,我们可不显示配置,它是默认使用org.springframework.context.annotation.AnnotationBeanNameGenerator生成器,也就是类名首字符小写的策略,如Performer类,它注册的Bean的ID为performer.并且可以自定义ID,如@Component("Joy").这边简单贴出这个默认生成器的实现。

复制代码


 
 
  1. public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  2. if (definition instanceof AnnotatedBeanDefinition) {
  3. String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
  4. if (StringUtils.hasText(beanName)) {
  5. // Explicit bean name found.
  6. return beanName;
  7. }
  8. }
  9. // Fallback: generate a unique default bean name.
  10. return buildDefaultBeanName(definition, registry);
  11. }

复制代码

 

 Spring除了实现了AnnotationBeanNameGenerator生成器外,还有个org.springframework.beans.factory.support.DefaultBeanNameGenerator生成器,它为了防止Bean的ID重复,它的生成策略是类路径+分隔符+序号

 ,如com.wjx.betalot.performer.impl.Sonnet注册为Bean的ID是com.wjx.betalot.performer.impl.Sonnet#0,这个生成器不支持自定义ID,否则抛出异常。同样贴出代码,有兴趣的可以去看下。

复制代码


 
 
  1. public static String generateBeanName(
  2. BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
  3. throws BeanDefinitionStoreException {
  4. String generatedBeanName = definition.getBeanClassName();
  5. if (generatedBeanName == null) {
  6. if (definition.getParentName() != null) {
  7. generatedBeanName = definition.getParentName() + "$child";
  8. }
  9. else if (definition.getFactoryBeanName() != null) {
  10. generatedBeanName = definition.getFactoryBeanName() + "$created";
  11. }
  12. }
  13. if (!StringUtils.hasText(generatedBeanName)) {
  14. throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
  15. "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
  16. }
  17. String id = generatedBeanName;
  18. if (isInnerBean) {
  19. // Inner bean: generate identity hashcode suffix.
  20. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
  21. }
  22. else {
  23. // Top-level bean: use plain class name.
  24. // Increase counter until the id is unique.
  25. int counter = -1;
  26. while (counter == -1 || registry.containsBeanDefinition(id)) {
  27. counter++;
  28. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
  29. }
  30. }
  31. return id;
  32. }

复制代码

 

resource-pattern:对资源进行筛选的正则表达式,这边是个大的范畴,具体细分在include-filter与exclude-filter中进行。

scoped-proxy: scope代理,有三个值选项,no(默认值),interfaces(接口代理),targetClass(类代理),那什么时候需要用到scope代理呢,举个例子,我们知道Bean的作用域scope有singleton,prototype,request,session,那有这么一种情况,当你把一个session或者request的Bean注入到singleton的Bean中时,因为singleton的Bean在容器启动时就会创建A,而session的Bean在用户访问时才会创建B,那么当A中要注入B时,有可能B还未创建,这个时候就会出问题,那么代理的时候来了,B如果是个接口,就用interfaces代理,是个类则用targetClass代理。这个例子出处:http://www.bubuko.com/infodetail-1434289.html。

scope-resolver:这个属性跟name-generator有点类似,它是基于接口ScopeMetadataResolver的,实现resolveScopeMetadata方法,目的是为了将@Scope(value="",proxyMode=ScopedProxyMode.NO,scopeName="")的配置解析成为一个ScopeMetadata对象,Spring这里也提供了两个实现,我们一起看下。首先是org.springframework.context.annotation.AnnotationScopeMetadataResolver中,

复制代码


 
 
  1. public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
  2. ScopeMetadata metadata = new ScopeMetadata();
  3. if (definition instanceof AnnotatedBeanDefinition) {
  4. AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
  5. AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
  6. if (attributes != null) {
  7. metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource()));
  8. ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
  9. if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
  10. proxyMode = this.defaultProxyMode;
  11. }
  12. metadata.setScopedProxyMode(proxyMode);
  13. }
  14. }
  15. return metadata;
  16. }

复制代码

 

对比一下org.springframework.context.annotation.Jsr330ScopeMetadataResolver中的实现:

复制代码


 
 
  1. public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
  2. ScopeMetadata metadata = new ScopeMetadata();
  3. metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
  4. if (definition instanceof AnnotatedBeanDefinition) {
  5. AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
  6. Set <String> annTypes = annDef.getMetadata().getAnnotationTypes();
  7. String found = null;
  8. for (String annType : annTypes) {
  9. Set <String> metaAnns = annDef.getMetadata().getMetaAnnotationTypes(annType);
  10. if (metaAnns.contains("javax.inject.Scope")) {
  11. if (found != null) {
  12. throw new IllegalStateException("Found ambiguous scope annotations on bean class [" +
  13. definition.getBeanClassName() + "]: " + found + ", " + annType);
  14. }
  15. found = annType;
  16. String scopeName = resolveScopeName(annType);
  17. if (scopeName == null) {
  18. throw new IllegalStateException(
  19. "Unsupported scope annotation - not mapped onto Spring scope name: " + annType);
  20. }
  21. metadata.setScopeName(scopeName);
  22. }
  23. }
  24. }
  25. return metadata;
  26. }

复制代码

 

ps:scope-resolver与scoped-proxy只能配置一个,配置了scope-resolver后你要使用代理,可以配置@Scope总的proxyMode属性项

use-default-filters:是否使用默认的扫描过滤。

<context:include-filter> :用来告知哪些类需要注册成Spring Bean,使用type和expression属性一起协作来定义组件扫描策略。type有以下5种

过滤器类型描述
annotation过滤器扫描使用注解所标注的那些类,通过expression属性指定要扫描的注释
assignable过滤器扫描派生于expression属性所指定类型的那些类
aspectj过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类
custom使用自定义的org.springframework.core.type.TypeFliter实现类,该类由expression属性指定
regex过滤器扫描类的名称与expression属性所指定正则表示式所匹配的那些类

 

 

 

 

 

 

 

要注意的是:若使用include-filter去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖

<context:exclude-filter>:与<context:include-filter> 相反,用来告知哪些类不需要注册成Spring Bean,同样注意的是:在use-default-filters="false"的情况下,exclude-filter是针对include-filter里的内容进行排除。

 

 

在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean

注意:如果配置了<context:component-scan>那么<context:annotation-config/>标签就可以不用再xml中配置了,因为前者包含了后者。另外<context:component-scan>还提供了两个子标签

1.        <context:include-filter>

2.       <context:exclude-filter>

在说明这两个子标签前,先说一下<context:component-scan>有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。所以如果仅仅是在配置文件中这么写

<context:component-scan base-package="tv.huan.weisp.web"/>

 Use-default-filter此时为true那么会对base-package包或者子包下的所有的进行java类进行扫描,并把匹配的java类注册成bean。

 

 可以发现这种扫描的粒度有点太大,如果你只想扫描指定包下面的Controller,该怎么办?此时子标签<context:incluce-filter>就起到了勇武之地。如下所示

<context:component-scan base-package="tv.huan.weisp.web .controller">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

但是因为use-dafault-filter在上面并没有指定,默认就为true,所以当把上面的配置改成如下所示的时候,就会产生与你期望相悖的结果(注意base-package包值得变化)

<context:component-scan base-package="tv.huan.weisp.web">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

此时,spring不仅扫描了@Controller,还扫描了指定包所在的子包service包下注解@Service的java类

此时指定的include-filter没有起到作用,只要把use-default-filter设置成false就可以了。这样就可以避免在base-packeage配置多个包名这种不是很优雅的方法来解决这个问题了。

另外在我参与的项目中可以发现在base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定<context:exclude-filter>来进行过滤,说明此包不需要被扫描。综合以上说明

Use-dafault-filters=”false”的情况下:<context:exclude-filter>指定的不扫描,<context:include-filter>指定的扫描

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值