源碼分析基於spring boot 2.1java
本文經過閱讀源碼,分析SpringBoot AutoConfigure功能的實現原理。
(源碼解析類文章建議在PC端閱讀)redis
SpringBoot中使用@EnableAutoConfiguration註解啓動AutoConfigure功能spring
@Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }
這裏起做用的其實是@Import和AutoConfigurationImportSelector。
@Import註解很是重要,它是SpringBoot中AutoConfiguration功能的基礎。數組
前面解析SpringBoot啓動過程的文章說過,SpringBoot啓動時會注入ConfigurationClassPostProcessor,該PostProcessor正是處理@Import的類。微信
ConfigurationClassPostProcessor#postProcessBeanFactory -> ConfigurationClassPostProcessor#processConfigBeanDefinitions框架
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // #1 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } if (configCandidates.isEmpty()) { return; } ... ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); //#2 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); //#3 parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); // #4 configClasses.removeAll(alreadyParsed); if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); // #5 alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); // #6 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); // #7 } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // #8 ... }
#1
檢查bean是否爲ConfigurationClass,這裏主要是檢查class註解信息(spring中將@Configuration標註的類歸類爲ConfigurationClass)#2
構建ConfigurationClassParser#3
解析ConfigurationClass#4
獲取結果,注意ConfigurationClassParser#getConfigurationClasses方法獲取ConfigurationClassParser的處理結果#5
獲取ConfigurationClass引入的Class,將其轉化爲BeanDefinition,並註冊到Spring上下文
最後構造bean,是在AbstractApplicationContext#refresh方法中,調用finishBeanFactoryInitialization,構建熱加載的單例bean時完成。#6
獲取新的BeanDefinition列表#7
若是前面的ConfigurationClass有引入了新的ConfigurationClass,添加到待處理集合#8
循環處理,直到待處理集合爲空spring-boot
ConfigurationClassParser#parse -> ConfigurationClassParser#processConfigurationClass源碼分析
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { // #1 return; } ... SourceClass sourceClass = asSourceClass(configClass); // #2 do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); // #3 } while (sourceClass != null); // #4 this.configurationClasses.put(configClass, configClass); // #5 }
#1
檢查ConfigurationClass是否存在@Conditional註解,若是存在,取註解中Condition條件判斷類進行判斷#2
將ConfigurationClass轉化爲SourceClass
SourceClass對Class元數據進行封裝,能夠兼容處理JVM加載的Class和ASM讀取的元數據,獲取元數據中註解,方法等信息#3
doProcessConfigurationClass方法很關鍵,處理@Component,@PropertySources,@ComponentScans,@Import,@ImportResource,帶@Bean的方法,接口及父類。#4
若是ConfigurationClass存在父類,doProcessConfigurationClass返回父類,這裏遞歸處理父類數據#5
將該ConfigurationClass加入configurationClasses,以便ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟使用
該方法是處理ConfigurationClass的入口,doProcessConfigurationClass中引入了新的ConfigurationClass,也會調用該方法處理。post
ConfigurationClassParser#doProcessConfigurationClassui
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { processMemberClasses(configClass, sourceClass); // #1 } ... processImports(configClass, sourceClass, getImports(sourceClass), true); // #2 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); // #3 if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); // #4 for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } processInterfaces(configClass, sourceClass); // #5 if (sourceClass.getMetadata().hasSuperClass()) { // #6 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; }
這裏只關注該方法AutoConfigure功能相關的代碼#1
若是Class存在@Component註解,會查詢Class的內部類,若是內部類也是ConfigurationClass,會調用processConfigurationClass方法處理內部類(注意,@Configuration註解上標識了@Component註解)#2
處理@Import註解#3
處理@ImportResource,添加引入資源信息到ConfigurationClass#importedResources,ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟會處理#4
選擇Class中存在@Bean標註的方法,加入到ConfigurationClass#beanMethods中#5
選擇接口中存在@Bean標註的方法,一樣加入到ConfigurationClass#beanMethods中#6
若是存在父類,返回父類到ConfigurationClassParser#processConfigurationClass中,遞歸處理父類。
ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // #1 Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (selector instanceof DeferredImportSelector) { // #2 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // #3 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); // #4 processImports(configClass, currentSourceClass, importSourceClasses, false); //#5 } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // #6 Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); //#7 } else { this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); //#8 } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
#1
@Import引入的類是ImportSelector實現類#2
DeferredImportSelector接口須要延遲處理,加入到deferredImportSelectorHandler中
ConfigurationClassParser#parse方法最後會調用deferredImportSelectorHandler#process方法處理DeferredImportSelector接口#3
調用ImportSelector#selectImports方法#4
使用ImportSelector#selectImports返回Class Name數組,加載對應的SourceClass#5
使用processImports方法繼續處理這些SourceClass#6
@Import引入的類是ImportBeanDefinitionRegistrar實現類#7
將該類加入到ConfigurationClass#importBeanDefinitionRegistrars中,ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟會處理#8
@Import引入的類是其餘類,轉發爲ConfigurationClass,使用processConfigurationClass方法處理
這裏對應了@Import註解的三種用法,引入ImportSelector,ImportBeanDefinitionRegistrar或者具體的ConfigurationClass。
@Import最後都要processConfigurationClass處理它引入的ConfigurationClass
回到ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟,
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions -> loadBeanDefinitionsForConfigurationClass
private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { ... if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); // #1 } for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); // #2 } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // #3 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); // #4 }
#1
註冊ConfigurationClass自身的BeanDefinition#2
註冊@Bean註解標識方法引入的bean#3
從@ImportResource引入的資源中讀取BeanDefinition#4
處理@Import引入的ImportBeanDefinitionRegistrar
@EnableAutoConfiguration註解引入的AutoConfigurationImportSelector,實現的是DeferredImportSelector接口
AutoConfigurationImportSelector#selectImports -> getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // #1 configurations = removeDuplicates(configurations); // #2 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // #3 configurations = filter(configurations, autoConfigurationMetadata); // #4 fireAutoConfigurationImportEvents(configurations, exclusions); // #5 return new AutoConfigurationEntry(configurations, exclusions); }
#1
從spring.factories文件中獲取@EnableAutoConfiguration對應的ConfigurationClass#2
ConfigurationClass除重#3
排除spring.autoconfigure.exclude配置的ConfigurationClass#4
使用spring.factories中配置的AutoConfigurationImportFilter的實現類(OnBeanCondition,OnClassCondition,OnWebApplicationCondition)過濾部分ConfigurationClass,這裏處理@ConditionalOnBean,@ConditionalOnClass,@ConditionalOnMissingClass等註解。
OnClassCondition能夠判斷當前Java環境中存在或者不存在某一個class,SpringBoot AutoConfigure功能能夠實現當咱們引入某個框架jar後,自動配置完成該框架的配置,正是經過該條件判斷類實現。
來看一個例子,RedisAutoConfiguration
@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } ... }
RedisAutoConfiguration是一個ConfigurationClass,他使用@Bean標識方法引入其餘bean。
(RedisAutoConfiguration在spring-boot-autoconfigure這個jar下的spring.factories文件中已經被配置爲@EnableAutoConfiguration的ConfigurationClass)
@ConditionalOnClass代表classpath只有存在RedisOperations這個類,RedisAutoConfiguration的配置才生效
(引入spring-data-redis的jar後有這個類了,RedisAutoConfiguration也就生效了)
@Import引入的 LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class用於與redis創建鏈接,並生成RedisConnectionFactory。
一樣,引入 Lettuce相關jar後,LettuceConnectionConfiguration生效,引入Jedis相關jar後,JedisConnectionConfiguration生效。
若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!