1 前言
我们都知道 SpringBoot 有个自动装配的机制,那你们知道平时如何使用么,以及他是什么时候执行的么,那么本节我们就来看看。
2 为什么要有自动装配
我们经历过SSM时期的时候,比如要引进Redis,是不是要先要导Maven依赖,还要进行大量的配置Bean,然后才能使用,而使用 SpringBoot 的方式,我们只需要引入一个 starter,然后配置redis信息,就可以使用了,这正是因为自动装配替我们做了繁琐的事。
3 自动装配不得不说的注解
说到自动装配原理,就要先看个注解 @Import,它的作用是什么呢?就是用来导入配置类或者一些需要前置加载的类,加载进我们的 Spring容器中。
可以看到该注解可以导入的三种类型:Configuration, ImportSelector, ImportBeanDefinitionRegistrar,用于注册某个类。
为什么要说这个注解呢?
因为自动装配入口就是通过 @Import 引入了 AutoConfigurationImportSelector 类,也就是自动装配的入口,然后 AutoConfigurationImportSelector 去帮我们完成一些类的装配也就是加载进容器里,才让我们更方便的去使用。 所以我们接下来就要看看 @Import 是什么时候执行的以及 AutoConfigurationImportSelector 又是如何解析执行的。
再另说一个小细节,我们启动类上的 @SpringBootConfiguration,其实他里边也是一个 @Configuration,为什么要说这个细节呢?因为下边提到的后置处理器会对配置类进行解析分析,所以我们的启动类里的 @Import继而被分析到呀。
4 @Import 解析执行时机
执行时机我先说一下是通过一个Bean工厂的后置处理器:ConfigurationClassPostProcessor 去解析执行的,那么该后置处理器又是何时执行的呢?就是 SpringBoot启动的时候刷新上下文的时候:
好了,那我们知道是刷新上下文的时候,通过执行Bean工厂的后置处理器来解析 @Import 继而执行的,那我们来看看 ConfigurationClassPostProcessor 的细节。
4.1 ConfigurationClassPostProcessor 继承关系
可以看到 ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,那我们接着来看看 postProcessBeanDefinitionRegistry 方法。
4.2 postProcessBeanDefinitionRegistry 处理
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 生成一个id,防止重复执行 int registryId = System.identityHashCode(registry); // 若重复就抛出异常 if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } // 表示此registry里的bd收集动作,已经做了 避免再重复收集此registry this.registriesPostProcessed.add(registryId); // 根据配置类,收集到所有的bd信息 也是@Import解析的入口时机 因为我们的启动类上也是一个@Configuration processConfigBeanDefinitions(registry); }
我们继续进去 processConfigBeanDefinitions 方法:
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); // 获取此时注册的bean String[] candidateNames = registry.getBeanDefinitionNames(); // 遍历当前容器中所有的BeanDefinition,从中寻找有@Configuration的类 for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 判断是否有@Configuration注解,如果有就加入候选列表 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 如果候选列表为空,就直接返回了 Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // 如果使用了@Order注解,就排个序 Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // 自定义的bean名称生成策略 Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // 下面开始解析候选列表 // 创建一个ConfigurationClassParser,后面的解析将委托给这个对象 // 创建配置类的解析器 Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse"); // 真正解析的入口,在这儿会对一系列注解进行解析如@Component,@import,@ComponentScan等 parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 如果BeanDefinitionReader为空,则创建一个,BeanDefinitionReader是用来加载BeanDefinition // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 前面ConfigurationClassParser#parse处理时遗留了三类类型,只将其解析并保存,并没有注册成BeanDefinition,在这个方法中进行处理 // 使用BeanDefinitionReader进行加载BeanDefinition this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end(); candidates.clear(); // 再次检查是否有遗漏的配置类未解析,若存在继续执行while循环 if (registry.getBeanDefinitionCount() > candidateNames.length) { // 如果发现当前容器中的BeanDefinition数量比上一轮解析完以后的数量多,说明这一轮解析了新的BeanDefinition // 这种情况需要对BeanDefinition列表逐个判断,如果其类型加了@Configuration注解,那么需要放入候选列表中,进行下一轮解析 String[] newCandidateNames = registry.getBeanDefinitionNames(); 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)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
整体的一个执行流程就是:
- 筛选获取配置类,对配置类进行排序操作
- 初始化Bean名称生成器
- 解析并注册所有的配置类
- 注册ImportRegistry
- 清理缓存
在解析和加载 BeanDefinition 时,需要通过特定的规则进行扫描, 主要看 ConfigurationClassParser#parse
和 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
两个方法。
4.3 parse 解析配置类
我们先来看下 parse 方法:
/** * 根据当前bean的类型去解析,不过最终都会进入processConfigurationClass方法 * @param configCandidates 配置类集合 */ public void parse(Set<BeanDefinitionHolder> configCandidates) { // 依次解析所有候选BeanDefinition for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { // 不同的bean定义类型执行不同的parse 方法 if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } // 处理前面在解析@Import注解中为DeferredImportSelector子类的对象 这儿也就是SpringBoot自动配置的入口 // 因为我们的 AutoConfigurationImportSelector 就是实现的 DeferredImportSelector 因为自动配置要被创建一般会有前提条件,所以推迟在这里处理 // public class AutoConfigurationImportSelector implements DeferredImportSelector this.deferredImportSelectorHandler.process(); }
根据不同的 BeanDefiniton 进行解析:
继续深入看 parse 方法,最后其实都会走到 processConfigurationClass 方法里:
4.3.1 processConfigurationClass 方法
那么看一下processConfigurationClass
方法的实现:
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { // 如果当前类使用了@Conditional注解,则需要根据条件判断是否要跳过该类的解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); //判断当前类是否已经解析过,防止重复解析 if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { //如果是被@Import注解导入的,那么记录一下 existingClass.mergeImportedBy(configClass); } // 直接结束了,不需要重复解析 Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // 如果当前类不是被其他类通过@Import注解导入的,说明其是被显式定义的(说明一个类被定义了两次),那么将旧的移除。(重新解析一次) // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass, filter); do { // 进行递归解析,直到该类没有父类为止 sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
整体的执行过程大致是:
-
判断当前类是否需要解析,判断委托给了
ConditionEvaluator
类进行处理,这个类型是根据当前类的@Conditional
注解进行处理的。 -
判断当前类是否已经被加载过,如果是被
@Import
依赖的,那么记录一下就直接返回不重复处理了;如果不是被@Import
依赖的,那么就再解析一遍(覆盖上一次的解析结果)。 -
具体解析的调用,是调用
doProcessConfigurationClass
方法进行处理,可以发现这个方法被一个循环所包围,因为方法会返回当前类型的父类,如果其父类存在,则会循环解析,知道不存在父类时,会返回null
。
4.3.2 doProcessConfigurationClass 方法
那么看一下doProcessConfigurationClass
方法的实现, 这个是具体处理 Configuration
的核心方法:
@Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { // 处理@Component注解 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // 递归处理当前类的内部类 Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass, filter); } // 处理@PropertySource注解 Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // 处理@ComponentScan注解 Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // @Conditional注解优先判断 for (AnnotationAttributes componentScan : componentScans) { // 根据@ComponentScan中的参数进行扫描,实际上是委托给ClassPathBeanDefinitionScanner处理 // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } // 如果新解析到的BeanDefinition使用了@Configuration,直接递归解析 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // 处理@Import注解 Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), filter, true); // 处理@ImportResource注解 Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 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); } } // 处理@Bean注解 Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { // 解析添加了@Bean注解的方法,并存起来,后面统一处理 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 递归查询并处理接口中的@Bean注解 Process default methods on interfaces processInterfaces(configClass, sourceClass); // 判断是否有父类,如果有父类,则返回,外层会递归调用;如果没有则返回null,结束解析 Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
@Component
里的 processMemberClasses
方法用来处理当前类的内部类,获取当前类的内部类,并循环递归调用processConfigurationClass
方法。
@PropertySource
用来实现将指定的配置文件加载到当前Spring
环境中。
@ComponentScan
的作用是自动扫描指定包中的所有类,并根据其是否有特定注解(例如@Service
、@Component
等)将其转化为BeanDefinition
加载当上下文中。ComponentScan
注解处理方式是将注解中配置的包路径依次委托给ClassPathBeanDefinitionScanner
中进行处理, 如果解析到了新的BeanDefinition
且使用了@Configuration
注解,直接调用parse
方法进行递归解析。
@ImportResource
可以将xml
类型的配置导入并解析到当前项目中,但是在此处并没有真正进行解析,也将其暂存起来,在后面会统一处理。
@Bean
是通过注解方式进行Bean
定义最常用的方式,在此处扫描所有加了@Bean
注解的方法并将其暂存,后面统一处理。
@Import
可以将其他类引入当前上下文中,在该方法中,先通过getImports
方法解析需要导入的类型,再调用processImports
方法处理这些类型,我们来看看:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, 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) { // 如果导入的是ImportSelector类型,则将其实例化,并调用其selectImports获取到真实需要导入的类名 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } // 如果是DeferredImportSelector的子类,就将其放入deferredImportSelectors中,延迟加载,后面会处理 if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 非DeferredImportSelector子类,直接调用selectImports进行处理 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } // 如果导入的类型是ImportBeanDefinitionRegistrar的子类,实例化并调用其Aware接口,将这些实例存起来,后面会统一处理 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } // 即不是ImportSelector也不是ImportBeanDefinitionRegistrar的情况,当做有@Configuration注解的类处理,递归解析 else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } 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(); } } }
在processImports
方法中,循环处理每个需要导入的类型,根据其类型分为三种处理方式:如果导入的类实现了ImportSelector
接口:ImportSelector
是一个动态导入接口,可以实现其selectImports
方法,在该方法中根据条件返回最终需要导入的类。而在当前方法的实现则是会实例化这个ImportSelector
子类,调用其selectImports
方法获取需要导入的类型,并递归调用processImports
方法。不过此处有个例外,如果导入类实现了DeferredImportSelector
接口,则不会在此处直接调用其selectImports
方法,而会延迟调用,在此处只是进行记录,具体调用时机就是在解析方法的最后一步,当前边的都扫描完了,就到它了。
AutoConfigurationImportSelector 就是实现的 DeferredImportSelector ,那么到这里解析执行的时机我们就找到了,就是在这个里了,我们进去看看:
4.3.3 process 方法
对于通过@Import
导入的DeferredImportSelector
子类延迟处理,在该方法中进行加载,处理逻辑基本与处理ImportSelector
类似。
public void process() { // 延迟的 import List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); // 执行 handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate<String> exclusionFilter = grouping.getCandidateFilter(); grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // 最后还是会调到 processImports processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
我们画个图理解一下:
看了这么多就一句话就是找@Import注解是什么时候解析执行的哈。
4.4 加载BeanDefinition
那顺便我们看一下解析完后的加载,ConfigurationClassBeanDefinitionReader#processConfigBeanDefinitions
方法中,前文我们已经把ConfigurationClassParser#parse
方法分析完了,接下来就是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
方法。这里调用的方法名称为loadBeanDefinitions
,直译过来就是加载BeanDefinition
,但其实根据上面的阅读可以发现,前面解析时已经加载了很多BeanDefinition
了,但是对于有些情况只做了记录,没有真正进行加载,而处理这些工作,正是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
做的事情。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { // 再遍历一次候选类,加载之前未完成的工作 loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } } /** * Read a particular {@link ConfigurationClass}, registering bean definitions * for the class itself and all of its {@link Bean} methods. */ private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } // 如果当前类是被@Import进来的,那么当前类型需要注册成BeanDefinition if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //依次加载@Bean注解的方法 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 加载@ImportResource注解对应的资源 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 处理通过@Import导入的ImportBeanDefinitionRegistrar类型 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
- 将被
@Import
引入的类自身注册成BeanDefinition
- 将被
@Bean
注解的方法解析成BeanDefinition
并注册 - 加载被
@ImportResource
依赖的配置文件 被@Import
导入的ImportBeanDefinitionRegistrar
类在此处处理
其中配置文件的加载,是被委托给对应的BeanDefinitionReader
加载的,例如xml
文件被委托给XmlBeanDefinitionReader
处理,这个过程与传统的Spring
项目的启动时加载配置文件的过程是一样的。对于ImportBeanDefinitionRegistrar
子类的处理过程是依次调用了其registerBeanDefinitions
方法,而其子类可以在这个方法中动态加载BeanDefinition
。
那么再过头来我们的 AutoConfigurationImportSelector 由于是延迟的,所以被暂时包装成 DeferredImportSelectorHolder,放进了 ConfigurationClassParser 解析器的 DeferredImportSelectorHandler 类型的 deferredImportSelectorHandler 对象的 deferredImportSelectors集合变量中。
5 AutoConfigurationImportSelector 解析过程
那接下来我们就来看看 AutoConfigurationImportSelector的执行,执行入口就是我们上边说的:
// ConfigurationClassParser#DeferredImportSelectorHandler#process public void process() { // 延迟的 ImportSelector 集合 List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; // 应该是防止死循环或者引入多次,直接赋值空 this.deferredImportSelectors = null; try { if (deferredImports != null) { // 不空的话,创建一个 DeferredImportSelectorGroupingHandler 处理器 DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); // 排序 deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 将每个 selector 注册进处理器 deferredImports.forEach(handler::register); // 处理器执行 handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } }
建议大家看的时候边调试边看,这块这个类内部类啥的好几个类的名字特别像,看的你会很晕,所以一定要调试哈。
可以看到该方法其实就是做了两步:
- 创建 DeferredImportSelectorGroupingHandler 处理器,并循环注册 Selector
- 执行处理器的 processGroupImports 方法
那我们先来看下注册。
5.1 ConfigurationClassParser#DeferredImportSelectorGroupingHandler#register
private class DeferredImportSelectorGroupingHandler { private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>(); private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>(); /** * 首先要知道 deferredImport 是什么 * 就是有两个属性的包装 * configurationClass 就是我们的启动类DemoApplication包装成的 ConfigurationClass对象里边有启动类的注解信息等 * importSelector 就是我们的 AutoConfigurationImportSelector * @param deferredImport */ public void register(DeferredImportSelectorHolder deferredImport) { /** * deferredImport.getImportSelector() 就是 AutoConfigurationImportSelector * AutoConfigurationImportSelector的 getImportGroup()方法 就是SpringBoot提供的 * * group 这里就是 AutoConfigurationImportSelector的内部类 AutoConfigurationGroup.class */ Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); /** * 放进 groupings Map中 * key就是AutoConfigurationGroup.class value就是 new DeferredImportSelectorGrouping(createGroup(group)) */ DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 把当前的延迟类信息放进 grouping中 grouping.add(deferredImport); /** * 存一份映射信息 * key就是 启动类的注解信息 * value就是包装成的 ConfigurationClass对象 */ this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } }
可以看到主要的就是:
/** * 放进 groupings Map中 * key就是AutoConfigurationGroup.class value就是 new DeferredImportSelectorGrouping(createGroup(group)) */ DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group)));
那么就来看看 createGroup(group),记住这里 group 类型是 AutoConfigurationImportSelector的内部类 AutoConfigurationGroup。
private Group createGroup(@Nullable Class<? extends Group> type) { // type不为空就是 AutoConfigurationGroup.class Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class); // 类实例化并且根据Aware赋值 Environment、ResourceLoader、BeanDefinitionRegistry return ParserStrategyUtils.instantiateClass(effectiveType, Group.class, ConfigurationClassParser.this.environment, ConfigurationClassParser.this.resourceLoader, ConfigurationClassParser.this.registry); }
那我们继续跟进去 ParserStrategyUtils.instantiateClass 方法:
static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment, ResourceLoader resourceLoader, BeanDefinitionRegistry registry) { Assert.notNull(clazz, "Class must not be null"); Assert.isAssignable(assignableTo, clazz); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader()); // 类型实例化也就是创建对象出来 T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader); // 就是根据类是否有某Aware调用相关的Aware方法赋值 ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader); return instance; }
这样通过注册方法,我们的 AutoConfigurationImportSelector 类里的 AutoConfigurationGroup 就被创建出来了哈。
5.2 ConfigurationClassParser#DeferredImportSelectorGroupingHandler#processGroupImports
注册完,那我们看看是如何执行的哈,接着看 processGroupImports 方法:
public void processGroupImports() { // this.groupings.values() 是不是就是 AutoConfigurationGroup for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { /** * 默认的过滤器 * private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className -> * (className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype.")); */ Predicate<String> exclusionFilter = grouping.getCandidateFilter(); // 获取import要引入的类集合,然后进行循环遍历 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // 最后还是会调到 processImports processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
可以看到就是通过 getImports 方法获取到要加载的类集合,然后循环调用 processImports,这个我们上边看过了哈就是分三种情况递归的又去执行了。那我们接下来就来看看 getImports 方法:
/** * Return the imports defined by the group. * @return each import with its associated configuration class */ public Iterable<Group.Entry> getImports() { /** * deferredImports 里有什么 就是我们第一步注册的时候放进去的 配置类信息 * group 就是我们的 AutoConfigurationImportSelector 类里的 AutoConfigurationGroup */ for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 执行加载类信息 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } // 得到一个类信息迭代器 return this.group.selectImports(); }
在 getImports 方法中就是执行 AutoConfigurationGroup的process方法加载要装配的类信息,然后调用 selectImports 得到一个要装配的类的迭代器,来迭代加载每个类。
最后回到我们的SpringBoot 里的 AutoConfigurationGroup类,我们来看看 process 方法:
/** * deferredImportSelector 就是 AutoConfigurationImportSelector 实例 * annotationMetadata 就是我们启动类上的注解信息 */ public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 收集自动配置的元组 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } /** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param autoConfigurationMetadata the auto-configuration metadata * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 获取 @EnableAutoConfiguration 注解信息 不存在会直接报错 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 通过 SpringFactoriesLoader.loadFactoryNames 加载 META-INF/spring.factories 下的 EnableAutoConfiguration 类信息 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 下边就是获取排除的有哪些等信息哈 我就不细看了哈 configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
最后 AutoConfigurationGroup的selectImports 方法返回迭代器就比较简单了,就是基本的集合操作:
@Override public Iterable<Entry> selectImports() { if (this.autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } Set<String> allExclusions = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); Set<String> processedConfigurations = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); processedConfigurations.removeAll(allExclusions); return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
到这里执行过程我们就看完了,你应该有点晕的,我们画个图来理解下:
6 小结
好了到这里,自动装配其实就是根据@Import来进行切入的,所以我们接着看了@import的解析时机以及执行时机,有理解不对的地方欢迎指正哈。
标签:装配,configClass,SpringBoot,自动,registry,注解,new,解析,class From: https://www.cnblogs.com/kukuxjx/p/17386860.html