springboot启动流程ioc容器refresh过程(下篇)
Posted lay2017
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot启动流程ioc容器refresh过程(下篇)相关的知识,希望对你有一定的参考价值。
所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
上一篇文章,我们知道了解析过程将从解析main方法所在的主类开始。在文章的最后我们稍微看了一下ConfigurationClassParser这个解析器的parse方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException processConfigurationClass(new ConfigurationClass(metadata, beanName));
本文将从这个parse方法继续下去,看看解析main方法所在的主类这个过程主要发生了什么。
跟进processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException // // 由main方法所在的主类开始,向超类逐层向上递归解析 SourceClass sourceClass = asSourceClass(configClass); do // 这里包含了解析单个配置类的核心逻辑 sourceClass = doProcessConfigurationClass(configClass, sourceClass); while (sourceClass != null); //
我们注意到,doProcessConfigurationClass方法将会完成解析的主要工作,但是又会返回一个新的sourceClass用于解析。而这个新的sourceClass会是当前上一个sourceClass的父类。所在解析过程是一个递归过程,由主类开始,向超类逐层向上递归解析处理。
继续跟进doProcessConfigurationClass方法,我们看看这个核心的解析逻辑。代码量对较多,我们只关注两个点
1)@ComponentScan注解解析,扫描并注册BeanDefinition
2)获取超类向上递归
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException // // 处理@ComponentScan注解 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) // 遍历@ComponentScan的属性值 for (AnnotationAttributes componentScan : componentScans) // 解析扫描 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // // // 判断是否有超类 if (sourceClass.getMetadata().hasSuperClass()) String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) this.knownSuperclasses.put(superclass, configClass); // 返回待解析的超类 return sourceClass.getSuperClass(); // 没有超类,则解析完毕 return null;
首先我们main方法所在的主类是被@SpringbootApplication注解所标注的,而@SpringbootApplication组合了@ComponentScan。所谓解析主类的时候将会处理@ComponentScan注解。解析@ComponentScan的主要工作的实现由ComponentScanAnnotationParser这个解析器来完成。通常这个解析器完成之后,被扫描到的BeanDefinition将会被注册到BeanFactory当中。
doProcessConfigurationClass方法的最后一部分是从当前被解析的类元数据中获取超类,如果超类存在且需要被解析那么就当做返回值返回回去,从而被外层的方法给递归处理。
@ComponentScan注解解析
下面,我们跟进ComponentScanAnnotationParser这个解析器的parse方法,看看@ComponentScan的处理过程
public Set<BeanDefinitionHolder> parse( AnnotationAttributes componentScan, final String declaringClass) ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); // Set<String> basePackages = new LinkedHashSet<>(); // 从basePackages配置获取扫描路径 String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); // 从basePackageClasses获取扫描路径 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) basePackages.add(ClassUtils.getPackageName(clazz)); if (basePackages.isEmpty()) // 默认添加当前被解析类的路径作为根路径 basePackages.add(ClassUtils.getPackageName(declaringClass)); // // 扫描目标路径 return scanner.doScan(StringUtils.toStringArray(basePackages));
这里获取了一个扫描器,然后找到了待扫描的路径,最后利用扫描器去扫描路径。
跟进doScan方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) // 扫描获取BeanDefinition Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) // if (checkCandidate(beanName, candidate)) BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注册BeanDefinition到BeanFactory registerBeanDefinition(definitionHolder, this.registry); return beanDefinitions;
我们看到,findCandidateComponents方法将会根据扫描路径获取BeanDefinition,而扫描出来的BeanDefinition将会进入注册方法registerBeanDefinition。
我们先跟进findCandidateComponents方法看看如何扫描获取
public Set<BeanDefinition> findCandidateComponents(String basePackage) if (this.componentsIndex != null && indexSupportsIncludeFilters()) return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); else // 进入 return scanCandidateComponents(basePackage);
再跟进scanCandidateComponents方法
private Set<BeanDefinition> scanCandidateComponents(String basePackage) Set<BeanDefinition> candidates = new LinkedHashSet<>(); try // 拼接出搜索路径,例如:classpath*:cn/lay/springbootlearn/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + ‘/‘ + this.resourcePattern; // 获取搜索路径下待处理资源 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); // for (Resource resource : resources) // if (resource.isReadable()) try MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) // 转化成BeanDefinition ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); // else // catch (Throwable ex) // else // catch (IOException ex) // return candidates;
basePackage将会被拼接成搜索路径,如:classpath*:cn/lay/springbootlearn/**/*.class。而getResources方法将会从搜索路径中获取相应的资源对象,这些资源对象并最终被读取并转化为BeanDefinition。
到这里,@ComponentScan扫描的Bean就已经成为了BeanDefinition,但是还有一步就是将BeanDefinition注册到BeanFactory当中。
我们回到doScan方法,并跟进registerBeanDefinition方法,看看注册过程
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
继续跟进registerBeanDefinition
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 省略
我们看到这里直接注册到了BeanDefinitionRegistry中去了,其实就是注册到BeanFactory当中。BeanFactory的默认实现类DefaultListableBeanFactory实现了BeanDefinitionRegistry,所以DefaultListableBeanFactory即是BeanDefinition的注册位置。
跟进DefaultListableBeanFactory的registerBeanDefinition方法
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); public void registerBeanDefinition( String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException // if (existingDefinition != null) // else if (hasBeanCreationStarted()) // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) this.beanDefinitionMap.put(beanName, beanDefinition); // else // // //
最终,也就是将BeanDefinition添加到一个key-value的集合当中,这样就完成了注册工作。
总结
到这里,ioc容器refresh过程部分就结束了。我们略过不少东西,将解析主类、解析@ComponentScan扫描Bean定义、注册到BeanFactory这个主要的流程过了一遍。当然,在这里可能还存在一个比较困惑的点。前面的文章中,我们提过几次:配置 -> BeanDefinition -> Bean这样一个过程。ioc的refresh过程却只有从配置 -> BeanDefinition这样一个过程,那么BeanDefinition -> Bean这个过程又在哪里呢?后面ioc容器注入部分将说明这部分内容。
以上是关于springboot启动流程ioc容器refresh过程(下篇)的主要内容,如果未能解决你的问题,请参考以下文章
springboot启动流程ioc容器refresh过程(下篇)
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~