<context:component-scan>标签解析原理

1,412 阅读5分钟

一、前言

Spring中除了在xml中定义bean,还支持注解方式,我们只需要使用<context:component-scan>标签声名bean所在的路径即可。如下图所示:


自定义标签解析中,分析了spring如何解析自定义标签,spring会根据自定义标签的命名空间选择对应的命名空间处理器,然后对标签进行解析,本文将以自定义标签<context:component-scan>为例,讲解Spring如何通过解析该标签,将对应package的类注入到容器中。

二、标签解析

承接上文,自定义标签的入口是DefaultBeanDefinitionDocumentReader.parseCustomElement():

@Nullable
  public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
      return null;
    }
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
    }
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  }​

对于<context:component-scan>标签来说,对应的NamespaceHandler是ContextNamespaceHandler。

NamespaceHandlerSupport:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    return (parser != null ? parser.parse(element, parserContext) : null);
  }

//查询该标签对应的BeanDefinitionParser
@Nullable
  private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    String localName = parserContext.getDelegate().getLocalName(element);
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
      parserContext.getReaderContext().fatal(
          "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
  }​

<context:component-scan>对应的BeanDefinitionParser是ComponentScanBeanDefinitionParser:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    //获取base-package属性,bean所在的package
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
​
    // 创建ClassPathBeanDefinitionScanner
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
  }

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    boolean useDefaultFilters = true;
    //对use-default-filters属性进行解析,默认为true,表示使用了注解@Component的bean,会注册到容器中
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
      useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }
​
    // 创建scanner,用于扫描类后注解。
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
​    //对资源进行筛选的正则表达式,用于过滤bean
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
      scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }
​
    try {
      //如果声明了name-generator属性,则使用给定的beanName生成策略,否则采用默认的策略,使用
      //className首字母小写作为beanName
      parseBeanNameGenerator(element, scanner);
    }
    catch (Exception ex) {
      parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
​
    try {
      //解析scoped-proxy和scope-resolver属性,平时很少使用,这里不做介绍,有兴趣的读者可以自己研究。
      parseScope(element, scanner);
    }
    catch (Exception ex) {
      parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
​    //解析配置的类型过滤器属性,包括include-filter和exclude-filter
    parseTypeFilters(element, scanner, parserContext);
    return scanner;
  }​

ClassPathBeanDefinitionScanner:

//创建ClassPathBeanDefinitionScanner,scanner的功能是扫描指定路径的bean,并注册到容器中。
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
        readerContext.getEnvironment(), readerContext.getResourceLoader());
  }​

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, @Nullable ResourceLoader resourceLoader) {
​
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
​    //如果use-default-filters为true,则注册默认过滤器。
    if (useDefaultFilters) {
      registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
  }​

ClassPathScanningCandidateComponentProvider

protected void registerDefaultFilters() {
    //支持Component注解
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
      //支持ManagedBean注解
      this.includeFilters.add(new AnnotationTypeFilter(
          ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.trace("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 {
      //支持Named注解
      this.includeFilters.add(new AnnotationTypeFilter(
          ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
      logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
    }
  }​

从代码中可以知道,默认情况下,spring会扫描带了Component、ManagedBean、Named注解的bean,注入容器。

ComponentScanBeanDefinitionParser:

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
    // Parse exclude and include filter elements.
    ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
    //获取conponent-scan标签的子标签
    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 {
          //如果子标签的名称是include-filter,解析该标签,添加IncludeFilter。
          if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
            TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
            scanner.addIncludeFilter(typeFilter);
          }
          //如果子标签的名称是exclude-filter,解析该标签,添加IncludeFilter。
          else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
            TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
            scanner.addExcludeFilter(typeFilter);
          }
        }
        catch (ClassNotFoundException ex) {
          parserContext.getReaderContext().warning(
              "Ignoring non-present type filter class: " + ex, parserContext.extractSource(element));
        }
        catch (Exception ex) {
          parserContext.getReaderContext().error(
              ex.getMessage(), parserContext.extractSource(element), ex.getCause());
        }
      }
    }
  }​

include-filter和exclude-filter的原理相同,可以支持annotationassignable的方式判断是否是否注入bean。下面以include-filter作为例子介绍:

protected TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader,
      ParserContext parserContext
) throws ClassNotFoundException {
​    //解析type属性
    String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
    //过滤策略
    String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
    expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
    //从annotation的角度过滤bean
    if ("annotation".equals(filterType)) {
      return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader));
    }
    else if ("assignable".equals(filterType)) {
      return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
    }
    else if ("aspectj".equals(filterType)) {
      return new AspectJTypeFilter(expression, classLoader);
    }
    else if ("regex".equals(filterType)) {
      return new RegexPatternTypeFilter(Pattern.compile(expression));
    }
    else if ("custom".equals(filterType)) {
      Class<?> filterClass = ClassUtils.forName(expression, classLoader);
      if (!TypeFilter.class.isAssignableFrom(filterClass)) {
        throw new IllegalArgumentException(
            "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
      }
      return (TypeFilter) BeanUtils.instantiateClass(filterClass);
    }
    else {
      throw new IllegalArgumentException("Unsupported filter type: " + filterType);
    }
  }​

由于use-default-filters默认支持使用了Component、Named、ManagedBean的bean,Controller、Service等注解使用了Component注解,因此使用了这两个注解的bean也会被扫描出来,如果我们只关注使用了Controller注解的bean,可以配置include-filter,type=annotion,expression=org.springframework.stereotype.Controller。

到此为止,ClassPathBeanDefinitionScanner已经配置好,下一步是扫描类,并注入bean到容器中。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
      //扫描basePackage,使用配置的include-filter、exclude-filter规则,而且类是最外层类、非抽象、非接口。
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
        //设置bean的作用域
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
        candidate.setScope(scopeMetadata.getScopeName());
        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
        if (candidate instanceof AbstractBeanDefinition) {
          postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
        }
        if (candidate instanceof AnnotatedBeanDefinition) {
          AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
        }
        if (checkCandidate(beanName, candidate)) {
          BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
          definitionHolder =
              AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
          beanDefinitions.add(definitionHolder);
          //注册beandefinition到容器中
          registerBeanDefinition(definitionHolder, this.registry);
        }
      }
    }
    return beanDefinitions;
  }​

ComponentScanBeanDefinitionParser

protected void registerComponents(
      XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
​
    Object source = readerContext.extractSource(element);
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
​
    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
      compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }
​
    // Register annotation config processors, if necessary.
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
      annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
    //如果context:component-scan标签中声明annotation-config=true(默认为true),
    //用于支持使用autowired、autowired注解注入成员变量
    if (annotationConfig) {
      Set<BeanDefinitionHolder> processorDefinitions =
          AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
      for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
      }
    }
​
    readerContext.fireComponentRegistered(compositeDef);
  }​

registerComponents()方法的主要作用:如果context:component-scan标签中声明了annotation-config为true,则在容器中注入以下bean等:

只有配置了annotation-config为true,才能通过注解注入依赖。


上面列出的三个bean都是BeanPostProcessor,执行时机是bean实例化后,用于bean的依赖注入、初始化等。

至此,Spring对<context:component-scan>标签的解析分析完毕。

三、总结

Spring中解析<context:component-scan>的逻辑如下:

  1. 根据<context:component-scan>的配置内容创建ClassPathBeanDefinitionScanner,设置包含、过滤bean的策略(use-default-filters、exclude-filter、include-filter),beanName的生成策略(name-generator)等;
  2. 使用创建好的Scanner扫描basePackage目录,并根据步骤1中设置的策略,过滤bean,设置beanName,生成beanDefinition,注册到容器中;
  3. 如果配置了annotation-config为true,则在容器中注册CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor、ConfigurationClassPostProcessor等BeanPostProcessor,用于支持依赖注解注入bean。