context:component-scan配置和use-default-filters详解
最近在SpringMVC项目中对controller进行aop切面处理时,发现切面无法切入,原来是WebApplicationContext和ApplicationContext存在继承关系,加载先后顺序会影响aop代理注入。于是展开对context:component-scan的深入研究
<context:component-scan>标签
对于context标签,都是交由ContextNamespaceHandler处理
@Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); }由源码可已看出,对component-scan的处理,是由ComponentScanBeanDefinitionParser类处理。
ComponentScanBeanDefinitionParser类通过configureScanner()方法,将配置文件中的信息封装成ClassPathBeanDefinitionScanner对象
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { boolean useDefaultFilters = true; if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } // Delegate bean definition registration to scanner class. ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader()); scanner.setEnvironment(parserContext.getReaderContext().getEnvironment()); scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) { scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE)); } try { parseBeanNameGenerator(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } try { parseScope(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } parseTypeFilters(element, scanner, parserContext); return scanner; }对属性USE_DEFAULT_FILTERS_ATTRIBUTE(use-default-filters)判断,调用
new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);构造方法,然后进行相应的后续处理,返回ClassPathBeanDefinitionScanner对象
其中的parseTypeFilters(element, scanner, parserContext);方法 对exclude 和 include filter elements进行过滤 代码如下
protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) { // Parse exclude and include filter elements. ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); NodeList nodeList = element.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String localName = parserContext.getDelegate().getLocalName(node); try { if (INCLUDE_FILTER_ELEMENT.equals(localName)) { TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); scanner.addIncludeFilter(typeFilter); } else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) { TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); scanner.addExcludeFilter(typeFilter); } } catch (Exception ex) { parserContext.getReaderContext().error( ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } } } }可以看到,会根据<content:exclude-filter>和<content:include-filter>标签对 ClassPathBeanDefinitionScanner中的两个属性进行操作,这两个属性就是
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>(); private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
ClassPathBeanDefinitionScanner继承自ClassPathScanningCandidateComponentProvider
/** * Create a ClassPathScanningCandidateComponentProvider with a {@link StandardEnvironment}. * @param useDefaultFilters whether to register the default filters for the * {@link Component @Component}, {@link Repository @Repository}, * {@link Service @Service}, and {@link Controller @Controller} * stereotype annotations * @see #registerDefaultFilters() */ public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) { this(useDefaultFilters, new StandardEnvironment()); }
/** * Register the default filter for {@link Component @Component}. * <p>This will implicitly register all annotations that have the * {@link Component @Component} meta-annotation including the * {@link Repository @Repository}, {@link Service @Service}, and * {@link Controller @Controller} stereotype annotations. * <p>Also supports Java EE 6"s {@link javax.annotation.ManagedBean} and * JSR-330"s {@link javax.inject.Named} annotations, if available. * */ @SuppressWarnings("unchecked") protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.debug("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>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.debug("JSR-330 "javax.inject.Named" annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
默认use-default-filters为true,此时加载默认的filters;通过注释可以看到,默认是对@Component、@Repository、@Controller和 JavaEE6的ManagedBean和javax.inject.Named进行扫描管理
我们再看 isCandidateComponent(MetadataReader metadataReader);方法/** * Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ 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)) { return isConditionMatch(metadataReader); } } return false; }
有注释可以看出,此方法用来决定一个是否不符合 exclude filter 和 符合include filter决定是否进行扫描。
总结:
<context:component-scan> 不配置 use-default-filters 或者配置 user-default-filters="true"
使用默认的filters 扫描包下的所有的注解
配置了 use-default-filters="false" 不使用默认的注解
无论时使用哪种都会经过 exclude-filter和include-filter的过滤