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

来源:互联网 发布:卖钣金展开软件 编辑:程序博客网 时间:2024/05/16 07:37

在Spring+SpringMVC+Mybatis的集成开发中经常会遇到事务配置不起作用等问题。


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

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

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

<context:component-scan base-package="com.jie.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.jie.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 0
原创粉丝点击