SpringBoot深入理解 -- @AliasFor注解的作用
SpringBoot源碼解析 -- SpringBoot啟動過程
SpringBoot源碼解析 -- AutoConfigure的實現(xiàn)原理
SpringBoot源碼解析 -- @ComponentScan的實現(xiàn)原理
SpringBoot源碼解析 -- @Value,@Autowired實現(xiàn)原理
SpringBoot源碼解析 -- Tomcat百宇,SpringMVC啟動
SpringBoot源碼解析 -- Logging仇矾,Environment啟動
源碼分析基于spring boot 2.1
SpringBoot中使用@EnableAutoConfiguration注解啟動AutoConfigure功能
@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鼻百,將其轉(zhuǎn)化為BeanDefinition绞旅,并注冊到Spring上下文
最后構造bean,是在AbstractApplicationContext#refresh方法中温艇,調(diào)用finishBeanFactoryInitialization因悲,構建熱加載的單例bean時完成。
#6
獲取新的BeanDefinition列表
#7
如果前面的ConfigurationClass有引入了新的ConfigurationClass中贝,添加到待處理集合
#8
循環(huán)處理囤捻,直到待處理集合為空
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轉(zhuǎn)化為SourceClass
SourceClass對Class元數(shù)據(jù)進行封裝蝎土,可以兼容處理JVM加載的Class和ASM讀取的元數(shù)據(jù),獲取元數(shù)據(jù)中注解绣否,方法等信息
#3
doProcessConfigurationClass方法很關鍵誊涯,處理@Component,@PropertySources蒜撮,@ComponentScans暴构,@Import,@ImportResource段磨,帶@Bean的方法取逾,接口及父類。
#4
如果ConfigurationClass存在父類苹支,doProcessConfigurationClass返回父類砾隅,這里遞歸處理父類數(shù)據(jù)
#5
將該ConfigurationClass加入configurationClasses,以便ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟使用
該方法是處理ConfigurationClass的入口债蜜,doProcessConfigurationClass中引入了新的ConfigurationClass晴埂,也會調(diào)用該方法處理。
ConfigurationClassParser#doProcessConfigurationClass
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的內(nèi)部類儒洛,如果內(nèi)部類也是ConfigurationClass,會調(diào)用processConfigurationClass方法處理內(nèi)部類(注意狼速,@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實現(xiàn)類
#2
DeferredImportSelector接口需要延遲處理,加入到deferredImportSelectorHandler中
ConfigurationClassParser#parse方法最后會調(diào)用deferredImportSelectorHandler#process方法處理DeferredImportSelector接口
#3
調(diào)用ImportSelector#selectImports方法
#4
使用ImportSelector#selectImports返回Class Name數(shù)組淮捆,加載對應的SourceClass
#5
使用processImports方法繼續(xù)處理這些SourceClass
#6
@Import引入的類是ImportBeanDefinitionRegistrar實現(xiàn)類
#7
將該類加入到ConfigurationClass#importBeanDefinitionRegistrars中郁油,ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5
步驟會處理
#8
@Import引入的類是其他類,轉(zhuǎn)發(fā)為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,調(diào)用ImportBeanDefinitionRegistrar#registerBeanDefinitions方法
@EnableAutoConfiguration注解引入的AutoConfigurationImportSelector棘街,實現(xiàn)的是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
#3
排除spring.autoconfigure.exclude配置的ConfigurationClass
#4
使用spring.factories中配置的AutoConfigurationImportFilter的實現(xiàn)類(OnBeanCondition蟆盐,OnClassCondition,OnWebApplicationCondition)過濾部分ConfigurationClass疏遏,這里處理@ConditionalOnBean锄开,@ConditionalOnClass颠通,@ConditionalOnMissingClass等注解。
OnClassCondition可以判斷當前Java應用中存在或者不存在某一個class痹愚,SpringBoot AutoConfigure功能可以實現(xiàn)當我們引入某個框架jar后,自動配置完成該框架的配置蛔糯,正是通過該條件判斷類實現(xiàn)拯腮。
來看一個例子,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文件中已經(jīng)被配置為@EnableAutoConfiguration的ConfigurationClass)
@ConditionalOnClass表明classpath只有存在RedisOperations這個類动壤,RedisAutoConfiguration的配置才生效
(引入spring-data-redis的jar后有這個類了,RedisAutoConfiguration也就生效了)
@Import引入的 LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class用于與redis建立連接飒箭,并生成RedisConnectionFactory狼电。
同樣,引入 Lettuce相關jar后弦蹂,LettuceConnectionConfiguration生效肩碟,引入Jedis相關jar后,JedisConnectionConfiguration生效凸椿。
如果您覺得本文不錯削祈,歡迎關注我的微信公眾號,您的關注是我堅持的動力!