SpringBoot自动装配原理
网上很多文章对于自动状态都是说的很含糊,而且是千篇一律。很多都是这个注解@EnableAutoConfiguration就自动完成了。怎么可能呢?注解永远都是死的,是需要某个代码去解析处理的
我今天就跟了下代码看看怎么回事
一 Configuration
@Configuration这个注解从Spring3就有了,可不是一个新东西。他的作用就是解放配置文件。
我们可以看到
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
就是一个@Configuration。
再看看 EnableAutoConfiguration,里面包含了一个 @Import
@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration
二 源码跟踪
我们要分析的代码入口在 ConfigurationClassPostProcessor 。确切的说是 ConfigurationClassParser.parse
这里说一下 ConfigurationClassPostProcessor
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException; }
也就是说作为 BeanDefinitionRegistryPostProcessor 的实现类,ConfigurationClassPostProcessor需要实现接口方法,而该接口方法的意义就是完成BeanDefinitioin的注册
下面再来看ConfigurationClassPostProcessor被调用的地方
大名鼎鼎的AbstractApplicationContext中的refresh方法
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//这个方法里会把springboot的启动类也就是有@SpringBootApplication这个注解的类解析成BeanDefinitionHolder // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
class PostProcessorRegistrationDelegate { public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>(); for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {//就在这里 BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } }
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
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);
}
}
processDeferredImportSelectors();//对AutoConfigurationImportSelector的解析在这里
}
上面的import只是提了一嘴,现在还没有走到那里,还是继续看怎么处理Configration这个注解的
继续往里面看 还是在 ConfigurationClassParser # processConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // 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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // 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)) { for (AnnotationAttributes componentScan : componentScans) { // 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(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true);
注意绿色的注解,processImports专门负责解析 @Import,其内部逻辑是把每一个@Import 注解的类都加载到 deferredImportSelectors
ConfigurationClassParser成员变量
@Nullable private List<DeferredImportSelectorHolder> deferredImportSelectors;
收集Import有必要说下,因为我一开始走了弯路,我一直在找 @EnableAutoConfiguration 这个注解的处理类,但是我想错了,其实这个类不需要解析。而是解析他里面的Import注解就行了。
比如说我们公司的启动类是这样的
@EnableLionConfig @SpringBootApplication( excludeName = {"${exclude.auto-configuration.name}"}, scanBasePackages = {"${scan.basePkg.path}", "com.ymm.trade", "com.ymm.architecture.*", "com.ymm.unify.authorization.*", "com.ymm.framework.healthcheck.client.*"} ) @ServletComponentScan @PropertySource( value = {"classpath:default.properties", "classpath:application.properties"}, factory = TradePropertySourceFactory.class ) @ImportResource( locations = {"${custom.resource.import:classpath*:spring/spring-*.xml}", "classpath*:authorization/pigeon-service.xml"}, reader = TradeImportResourceReader.class ) public class TradeBootStrap {
那么跟代码的结果就是 ConfigurationClassParser # collectImports
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) {//第一次是@EnableLionConfig String annName = annotation.getMetadata().getClassName(); if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited);//递归解析 } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
这里解释下 @EnableLionConfig 张这个样子
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @Import(LionConfigRegister.class) public @interface EnableLionConfig { }
直接跳到 ConfigurationClassParser # processDeferredImportSelectors
private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;//见截图 this.deferredImportSelectors = null; if (deferredImports == null) { return; } deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>(); Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>(); for (DeferredImportSelectorHolder deferredImport : deferredImports) { Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent( (group == null ? deferredImport : group), (key) -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } for (DeferredImportSelectorGrouping grouping : groupings.values()) { grouping.getImports().forEach((entry) -> { grouping.getImports()方法就会调用AutoConfigurationImportSelector.selectImports ConfigurationClass configurationClass = configurationClasses.get( entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); }
图1
继续看processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { 省略.... try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) {//import注解内参数的一种情况,importSelector // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//另一种情况ImportBeanDefinitionRegistrar // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions 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()); } 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)); } } }
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//getCandidateConfigurations方法就是去扫描spring.factories下面的KV键值对,把每个要加载的Configuration都加载进来 configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); }
图2
在这里有两个对象一个是我们写main方法所在的bean就是用@SpringBootApplication注解的bean,另一个就是AutoConfigurationImportSelector
所以自动装配发生作用的地点是在 ConfigurationClassPostProcessor 这个BeanFactoryPostProcessor对于注解@Configuration的处理中完成的