Spring配置解析之Component-scan解析

*基于3.1.2进行源码解析.

首先看一段项目中关于component-scan的配置:

<context:component-scan
        base-package="com.suning.rsc.server,com.suning.uops">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

这段配置配置相信使用过spring mvc的童鞋都很熟悉,上面配置的作用就是spring会扫描

com.suning.rsc.server和com.suning.uops这两个路径下的所有文件,将其中符合要求的文件注册的spring容器的bean来方便调用,exclude-filter则表明过滤掉添加了@Controller的文件。

         今天我们就结合源码来分析一下这段配置的具体实现:

         需要一提的是,解析spring配置文件的工作是在spring ioc容器创建的过程中进行的,这不是我们今天的工作重点,所以我们直接从解析这段配置的地方开始讲起:

  • BeanDefinitionParserDelegate#parseCustomElement

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
   String namespaceUri = getNamespaceURI(ele);
   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));
}

这里的两个入参简单说明一下,第二个BeanDefinition为null不用管,第一个Element对象是Spring将配置文件Xml解析为Document对象以后,解析其中的一个个节点Node,再强转为Element得来的,看下上面解析节点的那部分代码方便大家理解:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            else {
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

除了

public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ELEMENT = "alias";
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String BEAN_ELEMENT = "bean";
public static final String NESTED_BEANS_ELEMENT = "beans";

这四个属于spring默认的标签以外,其他标签的解析都是走的parseCustomElement()方法。

 

扫描二维码关注公众号,回复: 5917727 查看本文章

 我们仔细看下这个方法的代码:

parseCustomElement第一步:

String namespaceUri = getNamespaceURI(ele);

这个方法通过element对象解析得到对应的资源标记的地址,调试看一下具体的值:

ele:

namespaceUri:

 

parseCustomElement第二步:

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

这个方法将放回一个NameSpaceHandler对应,用来处理spring中的命名空间。在我们这里获取的具体对象是ContextNamespaceHandler,在解析namespaceUri的过程中,可以看到调用了一个初始化的方法:

针对我们这边的ContextNameSpaceHandler对象来说,它的init()方法主要对context下的八个命名空间注册不同的解析器,看下这部分的代码:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

   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());
   }

}

 这里的八个对象分别对应context下的8个不同namespace,我们这次解析的是component-scan标签,所以使用的解析对象是ComponentScanBeanDefinitionParser对象。对应的也就是parseCustomElement()方法的最后一行:

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

这里的具体实现就在ComponentScanBeanDefinitionParse中的parse()方法.这里还有一个参数this.readerContext,这个上下文对象里面主要包含这部分信息:

 

  • ComponentScanBeanDefinitionParse#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
   String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

   // Actually scan for bean definitions and register them.
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}

可以看到这个解析过程中有四个过程,我们先概括每个的作用,再一个个具体分析实现过程:

  • ComponentScanBeanDefinitionParse第一步

String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
      ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

这边主要就是解析配置文件中的

base-package="com.suning.rsc.server,com.suning.uops"

属性,会将里面的内容以一个字符串数组的形式返回,可以使用‘,’或者‘;’进行分隔;

 

  • ComponentScanBeanDefinitionParse第二步:

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

这个方法主要是创建一个路径的扫描器,会根据配置文件中的内容对扫描器进行属性的复制,用于后面bean的注册。

 

  • ComponentScanBeanDefinitionParse第三步:

Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);

这一步就是会扫描指定路径下的内容,完成bean的注册,返回的是一个存放了BeanDefinitionHolder对象的set集合,这个BeanDefinitionHolder可以当成BeanDefinition的

存放器,里面除了正常的beanname还有alias。

public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, String[] aliases) {
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");
   Assert.notNull(beanName, "Bean name must not be null");
   this.beanDefinition = beanDefinition;
   this.beanName = beanName;
   this.aliases = aliases;
}

 

  • ComponentScanBeanDefinitionParse第四步:

registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

这个方法会根据配置内容,选择性的注册组件。

 

 

到了这边的conponent-scan的主要工作就已经完成了,接下来就是具体看下方法的实现过程,先从获取scanner开始:

configureScanner(parserContext, element);

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
   XmlReaderContext readerContext = parserContext.getReaderContext();

   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(readerContext, useDefaultFilters);
   scanner.setResourceLoader(readerContext.getResourceLoader());
   scanner.setEnvironment(parserContext.getDelegate().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) {
      readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
   }

   try {
      parseScope(element, scanner);
   }
   catch (Exception ex) {
      readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
   }

   parseTypeFilters(element, scanner, readerContext, parserContext);

   return scanner;
}

可以看到这里面主要的内容就是给scanner设置属性,我们看几个主要部分的内容:

boolean useDefaultFilters = true;
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
   useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
}

这边是设置useDefaultFilters属性,这个属性默认是开启的,也就是use-default-filters=”true”,这个属性会在接下来实例化scanner的地方用到。

ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);

进去createScanner的方法内部,最终找到处理useDeFaultFilters的地方:\

ClassPathScanningCandidateComponentProvider#ClassPathScanningCandidateComponentProvider

public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
   if (useDefaultFilters) {
      registerDefaultFilters();
   }
   this.environment = environment;
}

根据useDefaultFilters属性,决定是否注册默认组件:

/**
 * 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>) 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.
   }
}

结合注释加代码,可以知道这边的处理是对@Component、@Repository、@Service、@Controller和 JavaEE6的ManagedBean和javax.inject.Named(如果有的话)进行扫描管理。这一部分内容可以在下面得到验证.

还有个需要关注的地方就是这个解析类型过滤器的地方:

parseTypeFilters(element, scanner, readerContext, parserContext);

这个方法会针对配置文件中的exclude-filter 和 include-filter属性,为扫描器加上对应的拦截器

protected void parseTypeFilters(
      Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext, 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);
               scanner.addIncludeFilter(typeFilter);
            }
            else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
               TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
               scanner.addExcludeFilter(typeFilter);
            }
         }
         catch (Exception ex) {
            readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
         }
      }
   }
}

通过解析节点,比较节点的名称,添加对应的filter:

if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
   TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
   scanner.addIncludeFilter(typeFilter);
}
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
   TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
   scanner.addExcludeFilter(typeFilter);
}

到了这个地方scanner就已经准备完毕了,我们看下这个scanner的样子:

excludeFilters的内容也和配置文件中的内容是一致的:

还记得上面说到的use-default-filter属性么,我们没有配置include-filter的内容,但是scanner中的includeFilters一样有值,就是因为默认的use-default-filters是true,我们看下里面的内容:

可以看到主要有两个内容,一个interface javax.annotation.ManagedBean 和interface org.springframework.stereotype.Component,和我们上面分析的内容是一样的,至于@Controller、@Service、@Repository没有展示,是因为这三个注解都可以算成@Component的子注解:

到此,扫描器的准备工作完成,接下来开始解析具体的文件。

 

  • scanner.doScan(basePackages);
/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         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);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }                 
   }
   return beanDefinitions;
}

我们把doscan这部分内容分成两部分来解析:

doscan第一部分:

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

doscan第二部分:循环上面获取到的BeanDefinition,找出其中符合要求的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);
   registerBeanDefinition(definitionHolder, this.registry);
}

我们先从dosacan第一部分开始说起,先找符合基本条件的候选bean:

this.resourcePatternResolver.getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               }
               else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}

findCandidateComponents这里面又可以分为三个部分:

findCandidateComponents第一步:首先会根据需要扫描的包名解析出具体搜索的路径:

String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
      resolveBasePackage(basePackage) + "/" + this.resourcePattern;

findCandidateComponents第二步:根据这个路径获取对应的的资源:

Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);

findCandidateComponents第三步:获取到resource以后,循环判断resource,符合要求的放入set<BeanDefinition>集合中:

MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
   ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
   sbd.setResource(resource);
   sbd.setSource(resource);
   if (isCandidateComponent(sbd)) {
      if (debugEnabled) {
         logger.debug("Identified candidate component class: " + resource);
      }
      candidates.add(sbd);
   }
   else {
      if (debugEnabled) {
         logger.debug("Ignored because not a concrete top-level class: " + resource);
      }
   }
}

我们具体看下findCandidateComponents的第二部分的内容:

PathMatchingResourcePatternResolver#getResources

public Resource[] getResources(String locationPattern) throws IOException {
   Assert.notNull(locationPattern, "Location pattern must not be null");
   if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
      // a class path resource (multiple resources for same name possible)
      if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
         // a class path resource pattern
         return findPathMatchingResources(locationPattern);
      }
      else {
         // all class path resources with the given name
         return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
      }
   }
   else {
      // Only look for a pattern after a prefix here
      // (to not get fooled by a pattern symbol in a strange prefix).
      int prefixEnd = locationPattern.indexOf(":") + 1;
      if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
         // a file pattern
         return findPathMatchingResources(locationPattern);
      }
      else {
         // a single resource with the given name
         return new Resource[] {getResourceLoader().getResource(locationPattern)};
      }
   }
}

这部分解析获取resource的内容可以分为四种分支:

我们这边走的是第一个分支:

PathMatchingResourcePatternResolver#findPathMatchingResources

/**
 * Find all resources that match the given location pattern via the
 * Ant-style PathMatcher. Supports resources in jar files and zip files
 * and in the file system.
 * @param locationPattern the location pattern to match
 * @return the result as Resource array
 * @throws IOException in case of I/O errors
 * @see #doFindPathMatchingJarResources
 * @see #doFindPathMatchingFileResources
 * @see org.springframework.util.PathMatcher
 */
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
   String rootDirPath = determineRootDir(locationPattern);
   String subPattern = locationPattern.substring(rootDirPath.length());
   Resource[] rootDirResources = getResources(rootDirPath);
   Set<Resource> result = new LinkedHashSet<Resource>(16);
   for (Resource rootDirResource : rootDirResources) {
      rootDirResource = resolveRootDirResource(rootDirResource);
      if (isJarResource(rootDirResource)) {
         result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
      }
      else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
         result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
      }
      else {
         result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
      }
   }
   if (logger.isDebugEnabled()) {
      logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
   }
   return result.toArray(new Resource[result.size()]);
}

findPathMatchingResources里面的内容主要分为两部分:

findPathMatchingResources第一部分:获取根目录下的资源:

Resource[] rootDirResources = getResources(rootDirPath);

这边的getResources方法就是递归调用上面的方法,会取出根目录下的所有目录

 

findPathMatchingResources第二部分:循环处理获取到的资源:

rootDirResource = resolveRootDirResource(rootDirResource);
if (isJarResource(rootDirResource)) {
   result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
   result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
}
else {
   result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}

findPathMatchingResources第二部分中会处理上面渠道的需要扫描的包的根目录的文件,首先对文件做解析,用于判断文件类型:

rootDirResource = resolveRootDirResource(rootDirResource);

接着对资源类型做校验,我们这里主要看第三种,也就是对非jar和不是vsf协议的处理:

result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));

doFindPathMatchingFileResources

/**
 * Find all resources in the file system that match the given location pattern
 * via the Ant-style PathMatcher.
 * @param rootDirResource the root directory as Resource
 * @param subPattern the sub pattern to match (below the root directory)
 * @return the Set of matching Resource instances
 * @throws IOException in case of I/O errors
 * @see #retrieveMatchingFiles
 * @see org.springframework.util.PathMatcher
 */
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
      throws IOException {

   File rootDir;
   try {
      rootDir = rootDirResource.getFile().getAbsoluteFile();
   }
   catch (IOException ex) {
      if (logger.isWarnEnabled()) {
         logger.warn("Cannot search for matching files underneath " + rootDirResource +
               " because it does not correspond to a directory in the file system", ex);
      }
      return Collections.emptySet();
   }
   return doFindMatchingFileSystemResources(rootDir, subPattern);
}

这边首先获取扫描包下的文件对象,也就是:

File rootDir;
rootDir = rootDirResource.getFile().getAbsoluteFile();

然后通过doFindMatchingFileSystemResources结合子目录名称,找出具体资源

 

doFindMatchingFileSystemResources

/**
 * Find all resources in the file system that match the given location pattern
 * via the Ant-style PathMatcher.
 * @param rootDir the root directory in the file system
 * @param subPattern the sub pattern to match (below the root directory)
 * @return the Set of matching Resource instances
 * @throws IOException in case of I/O errors
 * @see #retrieveMatchingFiles
 * @see org.springframework.util.PathMatcher
 */
protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
   if (logger.isDebugEnabled()) {
      logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
   }
   Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
   Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
   for (File file : matchingFiles) {
      result.add(new FileSystemResource(file));
   }
   return result;
}

这个方法会找出所有符合路径的要求的资源文件,这里面的内容也有很多,这里也暂不深入,只了解到这个,知道通过这边会返回所有符合条件的资源即可,后面实际需求中需要研究再继续深入。

 

 

到了这里我们已经得到了所有的候选者Resource[] resources,接下来我们要看下findCandidateComponents的第三部分,也就是循环处理resource对象,放入Set<BeanDefinition>集合中,留给doscan的第二部分处理:

if (resource.isReadable()) {
   try {
      MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
      if (isCandidateComponent(metadataReader)) {
         ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
         sbd.setResource(resource);
         sbd.setSource(resource);
         if (isCandidateComponent(sbd)) {
            if (debugEnabled) {
               logger.debug("Identified candidate component class: " + resource);
            }
            candidates.add(sbd);
         }
         else {
            if (debugEnabled) {
               logger.debug("Ignored because not a concrete top-level class: " + resource);
            }
         }
      }
      else {
         if (traceEnabled) {
            logger.trace("Ignored because not matching any filter: " + resource);
         }
      }
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(
            "Failed to read candidate component class: " + resource, ex);
   }
}
else {
   if (traceEnabled) {
      logger.trace("Ignored because not readable: " + resource);
   }
}

这里我们先对MetadataReader对象做个简单介绍:

/**
 * Simple facade for accessing class metadata,
 * as read by an ASM {@link org.springframework.asm.ClassReader}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
public interface MetadataReader {

   /**
    * Return the resource reference for the class file.
    */
   Resource getResource();

   /**
    * Read basic class metadata for the underlying class.
    */
   ClassMetadata getClassMetadata();

   /**
    * Read full annotation metadata for the underlying class,
    * including metadata for annotated methods.
    */
   AnnotationMetadata getAnnotationMetadata();

}

通过注释我们可以大概知道MetadataReader的作用应该是类似asm一样可以直接操作class文件。对这个有大概了解以后,我们就可以往下看了:

MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);

这里传入一个资源对象会返回一个对应的MetadataReader对象,我们具体看下getMetaDataReader的实现:

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   if (getCacheLimit() <= 0) {
      return super.getMetadataReader(resource);
   }
   synchronized (this.classReaderCache) {
      MetadataReader metadataReader = this.classReaderCache.get(resource);
      if (metadataReader == null) {
         metadataReader = super.getMetadataReader(resource);
         this.classReaderCache.put(resource, metadataReader);
      }
      return metadataReader;
   }
}

这里使用了

private final Map<Resource, MetadataReader> classReaderCache =
      new LinkedHashMap<Resource, MetadataReader>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
         @Override
         protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
            return size() > getCacheLimit();
         }
      };

作为缓存容器来存放Resource和MetadataReader的关联信息。

在初次获取时,调用父类的super.getMetadataReader(resource)方法获取MetadataReader:

SimpleMetadataReaderFactory#getMetadataReader

return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());

这里的具体实现就不在这里看了,因为asm相关的并不熟悉。

 

我们结合调试看下这个对象的内容:

Resource的内容:

获取到的MetadataReader的内容:

ClassMetadata中保存class对象的相关元数据

AnnotationMetadata中则保存所有注解相关的内容:

我们继续看findCandidateComponents的第三部分后面对MetadataReader对象的处理:

if (isCandidateComponent(metadataReader)) 

这里的处理就是看这个class是否可以匹配上之前配置的exclude-filter和include-filter过滤器:

/**
 * 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)) {
         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;
}

根据两个过滤器的结果比较返回这个类对象是否匹配

这里比较完以后,还有进一步的处理,同时会在进行一次判断:

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
   if (debugEnabled) {
      logger.debug("Identified candidate component class: " + resource);
   }
   candidates.add(sbd);
}

这里又看到一个新对象ScannedGenericBeanDefinition,看下这个对象是用来干什么的:

/**
 * Extension of the {@link org.springframework.beans.factory.support.GenericBeanDefinition}
 * class, based on an ASM ClassReader, with support for annotation metadata exposed
 * through the {@link AnnotatedBeanDefinition} interface.
 *
 * <p>This class does <i>not</i> load the bean {@code Class} early.
 * It rather retrieves all relevant metadata from the ".class" file itself,
 * parsed with the ASM ClassReader. It is functionally equivalent to
 * {@link AnnotatedGenericBeanDefinition#AnnotatedGenericBeanDefinition(AnnotationMetadata)}
 * but distinguishes by type beans that have been <em>scanned</em> vs those that have
 * been otherwise registered or detected by other means.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.5
 * @see #getMetadata()
 * @see #getBeanClassName()
 * @see org.springframework.core.type.classreading.MetadataReaderFactory
 * @see AnnotatedGenericBeanDefinition
 */
@SuppressWarnings("serial")
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

大概是说这个对象是对GenericBeanDefinition的扩展,实现了对annotation metadata的支持,

 

在第一次isComponentCandidate(metadataReader)后,会将metadataReader包装成ScannedGenericBeanDefinition,再判断一次isComponentCandidate(ScannedGenericBeanDefinition),我们看下此时的这个方法作用是什么:

/**
 * Determine whether the given bean definition qualifies as candidate.
 * <p>The default implementation checks whether the class is concrete
 * (i.e. not abstract and not an interface). Can be overridden in subclasses.
 * @param beanDefinition the bean definition to check
 * @return whether the bean definition qualifies as a candidate component
 */
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
   return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
}

 这里的意思是说这次的校验是保证:

这个类是实体类,也就是保证它不是一个接口,也不是一个抽象类

beanDefinition.getMetadata().isConcrete()
/**
 * Return whether the underlying class represents a concrete class,
 * i.e. neither an interface nor an abstract class.
 */
boolean isConcrete();

保证这个类是独立的,确定是顶级类

beanDefinition.getMetadata().isIndependent()
/**
 * Determine whether the underlying class is independent,
 * i.e. whether it is a top-level class or a nested class
 * (static inner class) that can be constructed independent
 * from an enclosing class.
 */
boolean isIndependent();

满足上面的两个条件以后,这个对象会被放入set<BeanDefinition>这个集合中,交给doscan的第二部分来处理。

这里想一下为什么对候选者需要做是否独立和是否是实体类的校验?带着这个疑问继续分析整个过程。

 

在这么多方法里面绕来绕去,现在我们终于看完了dosacan第一部分的内容,也就是已经获取了符合基本的一个set<BeanDefinition>的候选者集合。

 

我们接着解析doscan第二部分的内容:循环处理上面的set<BeanDefinition>:

for (BeanDefinition candidate : candidates) {
   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);
      registerBeanDefinition(definitionHolder, this.registry);
   }
}  

这部分内容都是为了最后注册beanDefinition服务,首先获取候选者的scope属性,默认都是sington实现的:

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());

简单看下解析的方法,这边也不深入了:

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
   ScopeMetadata metadata = new ScopeMetadata();
   if (definition instanceof AnnotatedBeanDefinition) {
      AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
      AnnotationAttributes attributes =
            attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
      if (attributes != null) {
         metadata.setScopeName(attributes.getString("value"));
         ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
         if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
            proxyMode = this.defaultProxyMode;
         }
         metadata.setScopedProxyMode(proxyMode);
      }
   }
   return metadata;
}

获取到bean对象的作用域(生命周期)以后,再生成beanname用于后面beanDefinition的注册:

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

接下来对candidate会做两个判断处理:

第一个判断:

if (candidate instanceof AbstractBeanDefinition) {
   postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}

这个判断用于设置默认属性:

/**
 * Apply further settings to the given bean definition,
 * beyond the contents retrieved from scanning the component class.
 * @param beanDefinition the scanned bean definition
 * @param beanName the generated bean name for the given bean
 */
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
   beanDefinition.applyDefaults(this.beanDefinitionDefaults);
   if (this.autowireCandidatePatterns != null) {
      beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
   }
}

看一下setAutowireCandidate()这个方法的介绍:

/**
 * Set whether this bean is a candidate for getting autowired into some other bean.
 */
public void setAutowireCandidate(boolean autowireCandidate) {
   this.autowireCandidate = autowireCandidate;
}

看到这里我们大概可以知道,在平时的使用通过@Autowire等注解可以注入bean的开关就是通过这个进行配置的。

 

第二个判断:

if (candidate instanceof AnnotatedBeanDefinition) {
   AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

这个判断用于处理@Primary 、@Lazy、 @DependsOn、@Role这四个注解的处理:

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
   AnnotationMetadata metadata = abd.getMetadata();
   if (metadata.isAnnotated(Primary.class.getName())) {
      abd.setPrimary(true);
   }
   if (metadata.isAnnotated(Lazy.class.getName())) {
      abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
   }
   if (metadata.isAnnotated(DependsOn.class.getName())) {
      abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
   }
   if (abd instanceof AbstractBeanDefinition) {
      if (metadata.isAnnotated(Role.class.getName())) {
         Integer role = attributesFor(metadata, Role.class).getNumber("value");
         ((AbstractBeanDefinition)abd).setRole(role);
      }
   }
}

关于这些注解具体的作用不在这边深入,这里了解这个方法的作用即可。

 

完成对candidate的判断处理以后,接下来就是注册beanDefinition的最后一次校验了:

checkCandidate(beanName, candidate)

这个方法主要就是判断这个beanDefinition是否可以被注册,如果有一个已存在的beanDefition则返回false,这种场景在重复扫描的时候就会出现:

/**
 * Check the given candidate's bean name, determining whether the corresponding
 * bean definition needs to be registered or conflicts with an existing definition.
 * @param beanName the suggested name for the bean
 * @param beanDefinition the corresponding bean definition
 * @return <code>true</code> if the bean can be registered as-is;
 * <code>false</code> if it should be skipped because there is an
 * existing, compatible bean definition for the specified name
 * @throws ConflictingBeanDefinitionException if an existing, incompatible
 * bean definition has been found for the specified name
 */
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
   if (!this.registry.containsBeanDefinition(beanName)) {
      return true;
   }
   BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
   BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
   if (originatingDef != null) {
      existingDef = originatingDef;
   }
   if (isCompatible(beanDefinition, existingDef)) {
      return false;
   }
   throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
         "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
         "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}

校验完候选beanDefinition以后,会将candidate包装成一个BeanDefinitionHolder对象:

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

这里看一下BeanDefinitionHolder:

/**
 * Holder for a BeanDefinition with name and aliases.
 * Can be registered as a placeholder for an inner bean.
 *
 * <p>Can also be used for programmatic registration of inner bean
 * definitions. If you don't care about BeanNameAware and the like,
 * registering RootBeanDefinition or ChildBeanDefinition is good enough.
 *
 * @author Juergen Hoeller
 * @since 1.0.2
 * @see org.springframework.beans.factory.BeanNameAware
 * @see org.springframework.beans.factory.support.RootBeanDefinition
 * @see org.springframework.beans.factory.support.ChildBeanDefinition
 */
public class BeanDefinitionHolder implements BeanMetadataElement {

   private final BeanDefinition beanDefinition;

   private final String beanName;

   private final String[] aliases;


   /**
    * Create a new BeanDefinitionHolder.
    * @param beanDefinition the BeanDefinition to wrap
    * @param beanName the name of the bean, as specified for the bean definition
    */
   public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
      this(beanDefinition, beanName, null);
   }

   /**
    * Create a new BeanDefinitionHolder.
    * @param beanDefinition the BeanDefinition to wrap
    * @param beanName the name of the bean, as specified for the bean definition
    * @param aliases alias names for the bean, or <code>null</code> if none
    */
   public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, String[] aliases) {
      Assert.notNull(beanDefinition, "BeanDefinition must not be null");
      Assert.notNull(beanName, "Bean name must not be null");
      this.beanDefinition = beanDefinition;
      this.beanName = beanName;
      this.aliases = aliases;
   }

这里截取到了构造方法,可以看到BeanDefinitionHolder中放的还是BeanDefinition对象,同时还包含的这个BeanDefinition的beanName和aliases的信息。

 

接着spring对BeanDefinitionHodler的处理:

definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

我们看下这个方法:

static BeanDefinitionHolder applyScopedProxyMode(
      ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

   ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
   if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
      return definition;
   }
   boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
   return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}

这个方法主要是设置beanDefinition的代理模式,为aop服务,我们这里暂时也不深入,了解到这边即可。

 

准备工作全部做完以后,将这个已经符合条件的beanDefinitionHolder对象放入一个set<BeanDefinitionHolder>集合中,这个集合对象后面会交给ComponentScanBeanDefinitionParse的第四步,也就是registerComponents()这个方法来调用处理。

Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();

beanDefinitions.add(definitionHolder);

最后我们看下doscan的的最后一个方法,注册beanDefinition:

registerBeanDefinition(definitionHolder, this.registry);

这里看到了registry对象,在上面的代码中它也出现过,我们在这个地方介绍一下这个对象:

private final BeanDefinitionRegistry registry;
/**
 * Interface for registries that hold bean definitions, for example RootBeanDefinition
 * and ChildBeanDefinition instances. Typically implemented by BeanFactories that
 * internally work with the AbstractBeanDefinition hierarchy.
 *
 * <p>This is the only interface in Spring's bean factory packages that encapsulates
 * <i>registration</i> of bean definitions. The standard BeanFactory interfaces
 * only cover access to a <i>fully configured factory instance</i>.
 *
 * <p>Spring's bean definition readers expect to work on an implementation of this
 * interface. Known implementors within the Spring core are DefaultListableBeanFactory
 * and GenericApplicationContext.
 *
 * @author Juergen Hoeller
 * @since 26.11.2003
 * @see org.springframework.beans.factory.config.BeanDefinition
 * @see AbstractBeanDefinition
 * @see RootBeanDefinition
 * @see ChildBeanDefinition
 * @see DefaultListableBeanFactory
 * @see org.springframework.context.support.GenericApplicationContext
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see PropertiesBeanDefinitionReader
 */
public interface BeanDefinitionRegistry extends AliasRegistry {

简单的说就是这个BeanDefinitionRegistry接口定义了对beanDefinition的一些操作,比如注册,获取,删除等等。

 

了解了基本作用以后,我们看下是怎么注册beanDefinition的:

/**
 * Register the specified bean with the given registry.
 * <p>Can be overridden in subclasses, e.g. to adapt the registration
 * process or to register further bean definitions for each scanned bean.
 * @param definitionHolder the bean definition plus bean name for the bean
 * @param registry the BeanDefinitionRegistry to register the bean with
 */
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

再看下BeanDefinitionReaderUtils中的注册方法内容:

/**
 * Register the given bean definition with the given bean factory.
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String aliase : aliases) {
         registry.registerAlias(beanName, aliase);
      }
   }
}

可以看到具体的注册过程分为两部分处理:

注册第一部分,用beanName注册beanDefinition:

// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

我们在看下这个的具体实现代码:

DefaultLisableBeanFactory#registerBeanDefinition

//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   synchronized (this.beanDefinitionMap) {
      Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
      if (oldBeanDefinition != null) {
         if (!this.allowBeanDefinitionOverriding) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                  "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                  "': There is already [" + oldBeanDefinition + "] bound.");
         }
         else {
            if (this.logger.isInfoEnabled()) {
               this.logger.info("Overriding bean definition for bean '" + beanName +
                     "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
         }
      }
      else {
         this.beanDefinitionNames.add(beanName);
         this.frozenBeanDefinitionNames = null;
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);

      resetBeanDefinition(beanName);
   }
}

可以看到注册的好的beanDefinition最后都会被存入beanDefinitionMap,

/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

后面需要取beanDefinition的话,也是从这个容器中取。

 

注册第二部分,用aliases也就是别名来分别注册这个beanDefinition:

// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
   for (String aliase : aliases) {
      registry.registerAlias(beanName, aliase);
   }
}

 

到了扫描到的文件中所有符合要求的对象都被作为beanDefinition注册到了spring ioc容器中,有关容器部分的内容这边也不做深入。

 

 

到了这边ComponentScanBeanDefinitionParse的第三步也已经分析完了,还有最后一步注册组件的内容。

 

  • registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
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));
   }
   if (annotationConfig) {
      Set<BeanDefinitionHolder> processorDefinitions =
            AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
      for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
         compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
      }
   }

   readerContext.fireComponentRegistered(compositeDef);
}

registerComponents这部分代码会包装一个CompositeComponentDefinition对象,然后会通过component-scan节点中的annotation-config的值来决定是否注册组件:

registerComponents第一步:包装CompositeComponentDefinition

Object source = readerContext.extractSource(element);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);

for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
   compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
}

我们先看下CompositeComponentDefinition:

/**
 * {@link ComponentDefinition} implementation that holds one or more nested
 * {@link ComponentDefinition} instances, aggregating them into a named group
 * of components.
 *
 * @author Juergen Hoeller
 * @since 2.0.1
 * @see #getNestedComponents()
 */
public class CompositeComponentDefinition extends AbstractComponentDefinition {

   private final String name;

   private final Object source;

   private final List<ComponentDefinition> nestedComponents = new LinkedList<ComponentDefinition>();


   /**
    * Create a new CompositeComponentDefinition.
    * @param name the name of the composite component
    * @param source the source element that defines the root of the composite component
    */
   public CompositeComponentDefinition(String name, Object source) {
      Assert.notNull(name, "Name must not be null");
      this.name = name;
      this.source = source;
   }


   public String getName() {
      return this.name;
   }

   public Object getSource() {
      return this.source;
   }


   /**
    * Add the given component as nested element of this composite component.
    * @param component the nested component to add
    */
   public void addNestedComponent(ComponentDefinition component) {
      Assert.notNull(component, "ComponentDefinition must not be null");
      this.nestedComponents.add(component);
   }

   /**
    * Return the nested components that this composite component holds.
    * @return the array of nested components, or an empty array if none
    */
   public ComponentDefinition[] getNestedComponents() {
      return this.nestedComponents.toArray(new ComponentDefinition[this.nestedComponents.size()]);
   }

}

CompositeComponentDefinition可以理解为一个容器,这个容器中可以放ConponentDedinition对象,有关这个对象我们可以直接看下调试的结果:

这个里面保存的就是ComponentDefinition。

 

registerComponents第二步:注册annotation相关的组件:

// Register annotation config processors, if necessary.
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
   annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
   Set<BeanDefinitionHolder> processorDefinitions =
         AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
   for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
      compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
   }
}

这里的ANNOTATION_CONFIG_ATTRIBUTE属性和前面提到的use-default-filters属性一样,默认值都是true。如果这个属性值为true,就会执行:

Set<BeanDefinitionHolder> processorDefinitions =
      AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);

看下这个方法的实现内容:

/**
 * Register all relevant annotation post processors in the given registry.
 * @param registry the registry to operate on
 * @param source the configuration source element (already extracted)
 * that this registration was triggered from. May be <code>null</code>.
 * @return a Set of BeanDefinitionHolders, containing all bean definitions
 * that have actually been registered by this call
 */
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, Object source) {

   Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);

   if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
   if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
   if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition();
      try {
         ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
         def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
      }
      catch (ClassNotFoundException ex) {
         throw new IllegalStateException(
               "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
      }
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   return beanDefs;
}

这里是注册相关的注解处理器,实现对@Configuration、@Bean、@Import、@Component、@ComponentScan、@ImportResource等注解的支持。

 

 

最后还有一个空方法用于BeanDefinition注册完成事件通知,但是并没有实现:

readerContext.fireComponentRegistered(compositeDef);
public void componentRegistered(ComponentDefinition componentDefinition) {
   // no-op
}

到此有关context:component-scan的解析过程就完成了,里面有很多地方都是点到即止,后续学习到相关内容再继续分析。

 

 

附上当时分析画的时序图:

猜你喜欢

转载自blog.csdn.net/qq_23585245/article/details/84106375