首页 > 其他分享 >context:component-scan扫描使用上的容易忽略的use-default-filters

context:component-scan扫描使用上的容易忽略的use-default-filters

时间:2023-04-20 11:39:53浏览次数:28  
标签:use Java scan default 代码 扫描 filters


评:
问题
如下方式可以成功扫描到@Controller注解的Bean,不会扫描@Service/@Repository的Bean。正确

Java代码 收藏代码
<context:component-scan base-package="org.bdp.system.test.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

但是如下方式,不仅仅扫描@Controller,还扫描@Service/@Repository的Bean,可能造成一些问题

Java代码 收藏代码
<context:component-scan base-package="org.bdp">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

这个尤其在springmvc+spring+hibernate等集成时最容易出问题的地,最典型的错误就是:
事务不起作用

这是什么问题呢?
分析
1、<context:component-scan>会交给org.springframework.context.config.ContextNamespaceHandler处理;

Java代码 收藏代码
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

2、ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理;
3、如果没有配置<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:

Java代码 收藏代码

protected void registerDefaultFilters() { 

this.includeFilters.add(new AnnotationTypeFilter(Component.class)); 

ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); 

try { 

 this.includeFilters.add(new AnnotationTypeFilter( 

 ((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false)); 

 logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); 

} 

catch (ClassNotFoundException ex) { 

 // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. 

} 

try { 

 this.includeFilters.add(new AnnotationTypeFilter( 

 ((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false)); 

 logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); 

} 

catch (ClassNotFoundException ex) { 

 // JSR-330 API not available - simply skip. 

}




可以看到默认ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描。如果细心,到此我们就找到问题根源了。


4、在进行扫描时会通过include-filter/exclude-filter来判断你的Bean类是否是合法的:

Java代码 收藏代码

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { 

 for (TypeFilter tf : this.excludeFilters) { 

 if (tf.match(metadataReader, this.metadataReaderFactory)) { 

 return false; 

 } 

 } 

 for (TypeFilter tf : this.includeFilters) { 

 if (tf.match(metadataReader, this.metadataReaderFactory)) { 

 AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); 

 if (!metadata.isAnnotated(Profile.class.getName())) { 

 return true; 

 } 

 AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class); 

 return this.environment.acceptsProfiles(profile.getStringArray("value")); 

 } 

 } 

 return false; 

}




首先通过exclude-filter 进行黑名单过滤;
然后通过include-filter 进行白名单过滤;
否则默认排除。

结论
Java代码 收藏代码
<context:component-scan base-package="org.bdp">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

为什么这段代码不仅仅扫描@Controller注解的Bean,而且还扫描了@Component的子注解@Service、@Reposity。因为use-default-filters默认为true。所以如果不需要默认的,则use-default-filters=“false”禁用掉。


请参考
《SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结》
《第三章 DispatcherServlet详解 ——跟开涛学SpringMVC》中的ContextLoaderListener初始化的上下文和DispatcherServlet初始化的上下文关系。

如果在springmvc配置文件,不使用cn.javass.demo.web.controller前缀,而是使用cn.javass.demo,则service、dao层的bean可能也重新加载了,但事务的AOP代理没有配置在springmvc配置文件中,从而造成新加载的bean覆盖了老的bean,造成事务失效。只要使用use-default-filters=“false”禁用掉默认的行为就可以了。

问题不难,spring使用上的问题。总结一下方便再遇到类似问题的朋友参考。

标签:use,Java,scan,default,代码,扫描,filters
From: https://blog.51cto.com/u_16080829/6209255

相关文章

  • Userful shell commoand in linux
     Userfulshellcommoandinlinuxcreatefolderlist:mkdir{com,xxx,yyy,zzz,domain,enums} createfolderstructure:mkdir-pcom/xxx/yyy/xzzz/domain/enums grantthepermisstionforthewholefolder(includesubfolder):chmod777-Rfolder/ removefolders:rm -Rf......
  • 华为防火墙 修改密码 过期时间的命令 manager-user password valid-days 0
    ......
  • some useful notes for cygwin
    usefulaliaslist:==================aliasgrep='grep--color'aliasl.='ls-d.*--color=tty'aliasla='ls-la'aliasll='ls-l'aliaslr='ls-lrt'aliasls='ls--color=auto'aliaswhich='......
  • ClickHouse数据迁移工具之clickhouse-copier
    ClickHouse数据迁移工具之clickhouse-copierclickhouse需要从单节点迁移至副本集群中,表结构统一修改为副本表网上搜到的迁移方式大致为三种。一、拷贝数据目录操作流程在源集群的硬盘上打包好对应数据库或表的data和metadata数据拷贝到目标集群对应的目录重启clickhou......
  • What's PLinq? how to use it?
    What'sPLinq?howtouseit?PLinqstandsfor"ParallelLINQ",whichisaparallelimplementationofLINQ(Language-IntegratedQuery)in.NET.ItallowsdeveloperstoperformLINQqueriesinparallelbyautomaticallypartitioningtheinput......
  • 本地升級stable-diffusion diffuser docker CUDA11.7 RTX2060
    1.0前言本地搭建stable-diffusiondiffuserdockerCUDA10.2RTX2060上次安裝的cuda10.2太舊了,升級cuda11.7順便填一下漏了的點。2.0卸載sudoapt-getremove--purge'^nvidia-.*'sudoapt-getremove--purge'^libnvidia-.*'sudoapt-getremove--purge'^cuda-.*&......
  • 云时代,MySQL到ClickHouse数据同步产品对比推荐
    ClickHouse在执行分析查询时的速度优势很好的弥补了MySQL的不足,但是对于很多开发者和DBA来说,如何将MySQL稳定、高效、简单的同步到ClickHouse却很困难。本文对比了NineData、MaterializeMySQL(ClickHouse自带)、Bifrost三款产品,看看他们在同步时的差异。对比结果概述整体上......
  • 好用的字符串搜索库 - Fuse.js
    https://github.com/krisk/Fuse OptionsFollow#BasicOptions#isCaseSensitiveType: booleanDefault: falseIndicateswhethercomparisonsshouldbecasesensitive.#includeScoreType: booleanDefault: falseWhetherthescoreshouldbeincludedinthe......
  • 报错解决:user.Case: (models.E020) The 'Case.check()' class method is currently ov
    Django在启动时报错,如下:user.Case:(models.E020)The'Case.check()'classmethodiscurrentlyoverriddenby<django.db.models.query_utils.DeferredAttributeobjectat0x0000020331E0AE20>.意思是说:在user.Case.check()在执行的过程中被【django.db.models.query_ut......
  • 定义一个User结构体
    d:一个数字,每个用户不同的idemail:email地址,一般网站的用户允许以email地址登录gender:性别,男or女QQ:QQ号码写一个函数,在User数组中查找某个id的User函数描述:User*find(User*all,intn,intid);其中,all:输入一个User数组n:数组长度id:待查找的id#include<iostream>......