springboot自动配置原理

    技术2022-07-11  108

    找到入口

    在每个springboot应用中,都有一个这个注解**@SpringBootApplication**

    @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }

    其中**@EnableAutoConfiguration**就是这一切的开始…

    @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }

    看到@EnableAutoConfiguration注解又标有@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个注解,顾名思义,@AutoConfigurationPackage注解肯定跟自动配置的包有关,而AutoConfigurationImportSelector则是跟SpringBoot的自动配置选择导入有关(Spring中的ImportSelector是用来导入配置类的,通常是基于某些条件注解@ConditionalOnXxxx来决定是否导入某个配置类)

    所以自动配置的入口也就是AutoConfigurationImportSelector了

    找到源头

    找到了自动配置的入口,同时也要知道怎么才能调用到里面的核心方法

    at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.selectImports(AutoConfigurationImportSelector.java:93) at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:386) at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:828) at org.springframework.context.annotation.ConfigurationClassParser.processDeferredImportSelectors(ConfigurationClassParser.java:563) at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:188) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:316) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:271) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:91) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:692) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:530)

    通过堆栈信息,可以看出是在刷新spring容器,并且调用bean工厂的后置处理器(invokeBeanFactoryPostProcessors)的时候执行了selectImports方法

    通过debug发现执行selectImports的类是ConfigurationClassPostProcessor,改后置处理器会把工作都交给ConfigurationClassParser去做

    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 ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } for (DeferredImportSelectorGrouping grouping : groupings.values()) {1》 grouping.getImports().forEach(entry -> {2》 ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } } grouping.getImports()拿到了所有待import的class数据,这里面就会调用selectImport,去拿配置文件里面的类名和注解信息执行import操作,会递归的判断,导入的class有没有import其他class,如果有就继续调用selectImport,没有就放入缓存configurationClasses中

    实现

    找到待import的类

    现在开始从源头往下,一直找到所有的实现原理

    public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {1this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); }

    代码的关键在《1》的位置,直接点进去看

    // DefaultDeferredImportSelectorGroup @Override public void process(AnnotationMetadata metadata, DeferredImportSelector selector) { for (String importClassName : selector.selectImports(metadata)) { this.imports.add(new Entry(metadata, importClassName)); } }

    在内部类DefaultDeferredImportSelectorGroup中,selector.selectImports(metadata)会拿到所有待import的class,然后遍历这些class,组装成Entry,放到列表里面,这个entry的结构是class名字+该class的注解信息

    最后返回上面的列表

    接下来重点关注**selector.selectImports(metadata)**里面的代码

    @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 《1》拿到配置,某个配置类的条件 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); // 《2》拿到所有的xxxAutoConfiguration List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); //《2》过滤,conditional起作用的地方 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } 读取配置文件,拿到类似于org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType的配置文件信息,信息包括某个配置类的注解,以及注解的条件拿到所有的xxxAutoConfiguration过滤配置文件中多余的配置类 private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { invokeAwareMethods(filter); // 《1》 boolean[] match = filter.match(candidates, autoConfigurationMetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { skip[i] = true; skipped = true; } } } if (!skipped) { return configurations; } List<String> result = new ArrayList<>(candidates.length); for (int i = 0; i < candidates.length; i++) { if (!skip[i]) { result.add(candidates[i]); } } if (logger.isTraceEnabled()) { int numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); } return new ArrayList<>(result); } 这里会传入几百个candidates(待确定需要需要配置的class)和autoConfigurationMetadata(前面几百个配置class)的条件信息,该方法里面会遍历所有的candidates,然后在autoConfigurationMetadata里面拿到他所对应的条件,进行match,如果匹配,则加入结果集

    还是用上面的org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType为例

    candidate = DataSourceAutoConfiguration

    条件 = DataSource + EmbeddedDatabaseType

    所以操作就是,去类路径下判断有没有DataSource和EmbeddedDatabaseType,如果都存在,则DataSourceAutoConfiguration就可以被放入结果集

    具体的代码实现如下:

    private static boolean isPresent(String className, ClassLoader classLoader) { if (classLoader == null) { classLoader = ClassUtils.getDefaultClassLoader(); } try { forName(className, classLoader); return true; } catch (Throwable ex) { return false; } }

    中间的调用过程就省略了,感兴趣可以自己去debug跟踪

    加入缓存

    实现的第二步,就是遍历所有待import的类,然后放入缓存,当然期间还回去判断待import的类是否还有@import注解

    ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); }

    在processImports里面,最关键的代码如下

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { //《1》 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()) { existingClass.mergeImportedBy(configClass); } return; } else { this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // 《2》 this.configurationClasses.put(configClass, configClass); } shouldSkip会判断这个类是否需要跳过,改方法会拿到类上的所有@Conditional注解,以及条件,然后去调用相应的ConditionOnXXX里面的方法,如果有条件不满如,就会跳过该类如果没有被跳过,就会被加入到configurationClasses缓存中

    再后来,就会进行 loadBeanDefinitionsForConfigurationClass…这里面又会调用shouldSkip…

    Processed: 0.013, SQL: 9