http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml
上一篇文章,咱們知道了解析過程將從解析main方法所在的主類開始。在文章的最後咱們稍微看了一下ConfigurationClassParser這個解析器的parse方法java
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); }
本文將從這個parse方法繼續下去,看看解析main方法所在的主類這個過程主要發生了什麼。spring
跟進processConfigurationClass方法springboot
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // // 由main方法所在的主類開始,向超類逐層向上遞歸解析 SourceClass sourceClass = asSourceClass(configClass); do { // 這裏包含了解析單個配置類的核心邏輯 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // }
咱們注意到,doProcessConfigurationClass方法將會完成解析的主要工做,可是又會返回一個新的sourceClass用於解析。而這個新的sourceClass會是當前上一個sourceClass的父類。所在解析過程是一個遞歸過程,由主類開始,向超類逐層向上遞歸解析處理。app
繼續跟進doProcessConfigurationClass方法,咱們看看這個核心的解析邏輯。代碼量對較多,咱們只關注兩個點this
1)@ComponentScan註解解析,掃描並註冊BeanDefinitionlua
2)獲取超類向上遞歸spa
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當中。code
doProcessConfigurationClass方法的最後一部分是從當前被解析的類元數據中獲取超類,若是超類存在且須要被解析那麼就當作返回值返回回去,從而被外層的方法給遞歸處理。component
下面,咱們跟進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容器注入部分將說明這部份內容。