context:component-scan标签的use-default-filters属性的作用以及原理分析

一、背景

  我们在Spring+SpringMVC+Mybatis的集成开发中,经常会遇到事务配置不起作用等问题,那么本文就来分析下出现这种问题可能的原因以及解决方式。

二、分析及原理窥探

  1.项目结构

  2.我们在spring-mvc.xml文件中进行如下配置,这种方式会成功扫描到带有@Controller注解的Bean,不会扫描带有@Service/@Repository注解的Bean,是正确的。

<context:component-scan base-package="com.hafiz.www.controller">   
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
</context:component-scan>  

  3.但是如下方式,不仅仅扫描到带有@Controller注解的Bean,还扫描到带有@Service/@Repository注解的Bean,可能造成事务不起作用等问题。

<context:component-scan base-package="com.hafiz.www">   
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
</context:component-scan>  

这是因为什么呢?下面让我们来从源码进行分析。

  1.<context:component-scan>会交给org.springframework.context.config.ContextNamespaceHandler处理.

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来调用如下代码:

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类是否是合法的:

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 进行白名单过滤,否则默认排除。

三、结论

  在spring-mvc.xml中进行如下配置:

<context:component-scan base-package="com.hafiz.www"> 
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
</context:component-scan>

则SpringMVC容器不仅仅扫描并注册带有@Controller注解的Bean,而且还扫描并注册了带有@Component的子注解@Service、@Reposity的Bean。因为use-default-filters默认为true。所以如果不需要默认的,则use-default-filters=“false”禁用掉。

  当我们进行上面的配置时,SpringMVC容器会把service、dao层的bean重新加载,从而造成新加载的bean覆盖了老的bean,但事务的AOP代理没有配置在spring-mvc.xml配置文件中,造成事务失效。解决办法是:在spring-mvc.xml配置文件中的context:component-scan标签中使用use-default-filters=“false”禁用掉默认的行为。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

《Spring Boot 实战:从0到1》第3章 零XML配置的Spring Boot Application第3章 零XML配置的Spring Boot

Spring Boot 提供了一种统一的方式来管理应用的配置,允许开发人员使用属性properties文件、YAML 文件、环境变量和命令行参数来定义优先级不同...

17440
来自专栏DT乱“码”

Spring的一些注解解析

@Repository、@Service、@Controller 和 @Component 将类标识为Bean spring 自 2.0 版本开始,陆续引入了一...

21390
来自专栏Ken的杂谈

Spring Boot入门教程3-1、使用Spring Boot+Freemarker模板引擎开发Web应用

在最早的Java Web应用中,最为广泛使用的就是JSP,但是JSP已经是陈旧的技术了,ken.io觉得JSP主要有三个问题: 1、视图代码不能与Java代码完...

12720
来自专栏一个会写诗的程序员的博客

《Spring Boot 实战开发》 附录 II : Spring Boot 2.0 新特性《Spring Boot 实战开发》 附录 II : Spring Boot 2.0 新特性Kotlin

本节我们介绍 Spring Boot 2.0 版本的众多新特性,内容包括了 M1~M7里程碑版本的核心新功能特性。不过,我们首先把对 Kotlin 的特性的支持...

21430
来自专栏Java技术栈

Spring Boot日志集成

Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starte...

36650
来自专栏微信公众号:Java团长

初识Spring Boot框架

前面的铺垫文章已经写了几篇了,主要是介绍了Spring和SpringMVC框架,小伙伴们在学习的过程中大概也发现了这两个框架需要我们手动配置的地方非常多,不过做...

11840
来自专栏Ken的杂谈

Spring Boot入门教程3-2、使用Spring Boot+Thymeleaf模板引擎开发Web应用

在最早的Java Web应用中,最为广泛使用的就是JSP,但是JSP已经是陈旧的技术了,ken.io觉得JSP主要有三个问题: 1、视图代码不能与Java代码完...

18830
来自专栏清晨我上码

第四节 微服务OTRS SpringCould使用

1.1. 启动一个服务注册中心,只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上...

11130
来自专栏编程

Spring-Data-REST轻松搞定RESTfulAPI

背景 昨天同事问我有没有研究过 ,没有~但是看名字就大概知道是做什么的(命名的重要性),因为之前有了解过 ,过一会发过两个截图过来。真的很强大,感觉这个在使用...

28760
来自专栏栗霖积跬步之旅

关于打包后提示无法连接到mongodb的情况

昨天晚上要和前端联调。 打完jar包后发现无法连接到测试环境的数据库。 就很尴尬,最后发现问题在于mongodb的URI写错了: 正确的URI格式:mongod...

38450

扫码关注云+社区

领取腾讯云代金券