您好,登錄后才能下訂單哦!
小編給大家分享一下SpringBoot如何解析配置類以及集成第三方配置,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
SpringBoot作為Java領域非常流行的開源框架,集成了大量常用的第三方庫配置,Spring Boot應用中這些第三方庫幾乎可以是零配置的開箱即用,大部分的 Spring Boot 應用都只需要非常少量的配置代碼,開發者能夠更加專注于業務邏輯。SpringBoot上手快,但是如果你的項目中業務場景需要一些特殊定制,甚至對源碼進行定制化,那這時候了解原理就變成必需的了,只有充分了解源碼,知道框架底層的工作原理,才能對源碼中原有的機制進行修改 / 擴展等等。
在SpringBoot中推薦基于Java Config的方式來代替傳統的XML方式去引入Bean,本文就是分析SpringBoot如何解析這些配置類,為容器中注入我們自定義的以及SpringBoot為我們提供的Bean。SpringBoot版本基于2.1.7.RELEASE。
// 通常一個SpringBoot工程會含有這樣一個主配置類,它位于我們項目的根包下,通過啟動這個main方法就可以啟動我們的項目 // 下面我們先分析@SpringBootApplication注解有哪些作用,在第二節中分析run方法,在run方法中會進行配置類的解析 @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
// 點擊@SpringBootApplication進去發現它其實是由三個核心注解構成的,下面分別講解這三個注解 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
// 點進去發現它其實就是一個@Configuration注解,SpringBoot解析到就會知道這是一個配置類,會給容器中引入一些bean // 一個被@Configuration標注的類,相當于一個applicationContext.xml文件 // @Configuration點進去發現其實就是一個@Component注解 @Configuration public @interface SpringBootConfiguration { }
2、@EnableAutoConfiguration注解
// 結合下面@AutoConfigurationPackage注解,發現@EnableAutoConfiguration注解就是通過@Import注解給容器中引入了兩個bean, // 分別是AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar,通過這兩個類可以給容器中引入更多的類 // 下面先介紹下@Import注解的使用 @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { } @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; // 往容器中新增BeanDefinition GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(Chicken.class); registry.registerBeanDefinition("beanFactoryPostProcessor-Chicken", beanDefinition); // 修改容器中原有的BeanDefinition BeanDefinition snake = registry.getBeanDefinition("snake"); snake.setLazyInit(true); } }
@Component public class CatBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Cat){ Cat cat = (Cat) bean; cat.setName("changeNameCat"); } return bean; } }
第一節是SpringBoot解析自動配置類會用到的一些知識點,下面我們來看SpringBoot解析配置類的具體過程。上圖是SpringBoot啟動流程圖,其中在refreshContext的第五步會調用容器的BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法。其中有一個是ConfigurationClassPostProcessor,它是在創建ConfigurableApplicationContext時設置到容器中的,如下所示。
// 圖中說的創建ConfigurableApplicationContext,默認創建的是普通的Servlet Web容器,就是下面這個 // 通過反射創建會走到其默認的構造函數 public AnnotationConfigServletWebServerApplicationContext() { // 這里面進去會走到下面代碼 this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } // 走到這里 registerAnnotationConfigProcessors(registry, null); // 走到這里 // 向容器中注入一個ConfigurationClassPostProcessor,它是BeanFactoryPostProcessor if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 向容器中注入一個AutowiredAnnotationBeanPostProcessor,它是BeanPostProcessor,用于解決依賴注入的 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); }
上面說到在refreshContex中的第五步時,會調用容器中的BeanFactoryPostProcessor
的postProcessBeanDefinitionRegistry方法。其中有一個是ConfigurationClassPostProcessor,這是我們解析自動配置類的入口,下面分析其postProcessBeanDefinitionRegistry方法。
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 刪掉一些非關鍵代碼 processConfigBeanDefinitions(registry); }
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<beandefinitionholder> configCandidates = new ArrayList<>(); // 獲取容器中已注冊的bean名字,見下圖,注意,這里容器中這些BeanDefinition都是容器初始化過程中容器添加進去的 // 不是我們業務代碼的beanDefinition,這段代碼其實是連貫的,為了注釋圖片方便才分開 String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { // 獲取BeanDefinition BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 判斷這個BeanDefinition的configurationClass屬性是不是full或者lite,如果是認為已經處理過了,第一次時默認為空, // 走下面分支 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { // 打印日志記錄下 } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // 1) 下面先分析下這個checkConfigurationClassCandidate方法,這邊看方法名也可以猜到是檢測該類是不是配置類 // 是配置類的意思就是它會給容器中引入bean,這個方法判斷主要就是看這個類的元信息中有沒有@Configuration注解 // 有沒有@Component注解、有沒有@ComponentScan、@Import、@ImportResource注解,有沒有@Bean方法 // 構造一個BeanDefinitionHolder,放入configCandidates中 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found // 上圖中容易默認已經引入了7個BeanDefinition,經過上面檢測發現默認就一個符合條件的配置類,即我們的主配置類 // 這里面configCandidates就一個,就是SpringBootApplication if (configCandidates.isEmpty()) { return; } // 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); }); // 刪掉部分代碼 // 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()); // 循環處理直到candidates.isEmpty() do { // 這邊開始解析,對應步驟4 parser.parse(candidates); parser.validate(); // 取出第四步解析得到的一些configurationClasses集合 Set<configurationclass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 刪除一部分代碼 // 這邊也會去加載BeanDefinition,對應圖中步驟五 this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); } while (!candidates.isEmpty()); }
在配置類解析流程圖中,第二步,會獲取容器中已經注冊的BeanDefinition,放入candidateNames中,然后依次遍歷這些BeanDefinition,判斷它有沒有被處理過,如果處理過就不管,否則通過checkConfigurationClassCandidate方法去判斷它是不是配置類,判斷方法如下。通過閱讀這段代碼,發現如果一個類上面有@Configuration注解、或者有@Component、@ComponentScan、@Import、@ImportResource注解、或者有@Bean標注的方法,則認為它是一個配置類。默認情況下,走到這里時最終只有一個candidateName符合,它是我們的主配置類,也就是SpringbootApplication這個Bean。
ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory); public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { String className = beanDef.getBeanClassName(); // 獲取下類名,如果類名為空或者該類為工廠類 if (className == null || beanDef.getFactoryMethodName() != null) { return false; } // 獲取類的元數據信息 AnnotationMetadata metadata; // 上圖的7個candidateNames中只有一個springbootApplication是AnnotatedBeanDefinition,其余全返回false if (beanDef instanceof AnnotatedBeanDefinition && className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { // Can reuse the pre-parsed metadata from the given BeanDefinition... // springbootApplication走到這里 metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); } else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { // Check already loaded Class if present... // since we possibly can't even load the class file for this Class. Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); metadata = new StandardAnnotationMetadata(beanClass, true); } else { try { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); // 讀取類的元數據信息,這里面包括注解等信息 metadata = metadataReader.getAnnotationMetadata(); } catch (IOException ex) { return false; } } // metadata.isAnnotated(Configuration.class.getName()),這個就是判斷類上面有沒有@Configuration注解 if (isFullConfigurationCandidate(metadata)) { // 如果true的話設置下這個屬性,那么就標記為處理過了 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); } else if (isLiteConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); } else { // 其余6個返回false return false; } // It's a full or lite configuration candidate... Let's determine the order value, if any. Integer order = getOrder(metadata); if (order != null) { // 獲取下類上的@Order信息 beanDef.setAttribute(ORDER_ATTRIBUTE, order); } return true; }
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) { // Do not consider an interface or an annotation... if (metadata.isInterface()) { return false; } // Any of the typical annotations found? for (String indicator : candidateIndicators) { // 判斷下類上面有沒有這幾個注解 if (metadata.isAnnotated(indicator)) { return true; } } // Finally, let's look for @Bean methods... try { // 判斷有沒有@Bean的方法 return metadata.hasAnnotatedMethods(Bean.class.getName()); } return false; } } private static final Set<string> candidateIndicators = new HashSet<>(); static { candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName()); }
public void parse(Set<beandefinitionholder> configCandidates) { // 刪除部分代碼,實際執行時這里的configCandidates就一個springBootApplication代表的主配置類 for (BeanDefinitionHolder holder : configCandidates) { // 獲取BeanDefinition BeanDefinition bd = holder.getBeanDefinition(); // 我們的SpringBootApplication會走到這邊,下面先分析這邊 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } // (**)處這邊也要留意下,這邊會處理DeferredImportSelector,我們前面說的AutoConfigurationImportSelector就是在這邊處理 // 給容器中導入xxxAutoConfiguration this.deferredImportSelectorHandler.process(); } protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); } protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // 根據當前類上面的@Conditional注解標注的條件判斷是否要解析這個配置類 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } // 以configClass作為key去獲取,第一次來肯定是獲取不到的,走下面邏輯 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // 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); do { // 這里是重點,里面具體分為8大步驟,單獨拿一小節分析 // b) doProcessConfigurationClass sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // 放入configurationClasses中 this.configurationClasses.put(configClass, configClass); }
// 上面的asSourceClass最終其實就是封裝了一個SourceClass對象 public SourceClass(Object source) { this.source = source; if (source instanceof Class) { this.metadata = new StandardAnnotationMetadata((Class<?>) source, true); } else { this.metadata = ((MetadataReader) source).getAnnotationMetadata(); } }
下面這個doProcessConfigurationClass具體分為8個小步驟去解析,對應步驟四種的A-H步驟
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // 判斷這個類上面有沒有@Component注解 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first // 如果有的話,遍歷其內部類,然后也是調用doProcessConfigurationClass遞歸處理 processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations // 處理PropertySource注解,之前講解屬性配置也分析過,就是將該注解對應的屬性文件加載到Environment中 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } } // Process any @ComponentScan annotations // 處理@ComponentScan注解,將其指定的包下的bean注冊到框架中 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 // 處理Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations // 處理@ImportResource注解,可以通過它來指定xml文件,BeanFactory就會讀取這個xml文件將bean注冊進去 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); } } // Process individual @Bean methods // 處理我們的類中使用@Bean注解的方法,添加到configClass的beanMethod中 Set<methodmetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 處理接口的默認方法,遍歷這個類的接口,判斷有沒有使用@Bean注解的非抽象方法,添加到configClass的beanMethod中 processInterfaces(configClass, sourceClass); // Process superclass, if any // 遞歸處理父類,這邊返回父類上層方法會遞歸處理 if (sourceClass.getMetadata().hasSuperClass()) { // 判斷父類不為null且不在knownSuperclasses中且不以Java開頭 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; }[object Object]
if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Register member (nested) classes that happen to be configuration classes themselves. private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { Collection<SourceClass> memberClasses = sourceClass.getMemberClasses(); // 判斷是否有內部類,沒有的話直接不處理 if (!memberClasses.isEmpty()) { List<SourceClass> candidates = new ArrayList<>(memberClasses.size()); for (SourceClass memberClass : memberClasses) { // 判斷是否是配置類,判斷也很簡單,之前分析過,判斷類上面有沒有@Configuration注解、@Import、@ImportResource // @Component、@ComponentScan以及@Bean標注的方法 if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) { // 加入到candidates中然后排個序 candidates.add(memberClass); } } OrderComparator.sort(candidates); for (SourceClass candidate : candidates) { // 防止A引入防止A引入B,B引入A if (this.importStack.contains(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { // 放入棧中并遍歷處理這些配置類,也是遞歸處理,調用之前的doProcessConfigurationClass處理這個配置類 processConfigurationClass(candidate.asConfigClass(configClass)); } finally { this.importStack.pop(); } } } } }
@SpringBootApplication @PropertySource({"demo.properties"}) public class Springboot2Application {
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { // 這邊就不進去看了,主要是讀取@PropertySource注解指定的文件,將其封裝成一個屬性集放入到環境中 processPropertySource(propertySource); } }
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 // 下面先分析這個parse方法 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()); } } } }
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); // 設置一個bean名字生成器,默認就是使用org.springframework.beans.factory.support.BeanNameGenerator scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); // 就是默認的 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); // 理解是元數據解析器 scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } // 設置下掃描的資源模式,是**/*.class scanner.setResourcePattern(componentScan.getString("resourcePattern")); // 添加IncludeFilter和ExcludeFilter for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } // 設置是否懶加載 boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } // 解析掃描的包路徑加入到basePackages中 Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { // 解析basePackageClasses所在的包并加入到basePackages basePackages.add(ClassUtils.getPackageName(clazz)); } // 如果是空的,將聲明該注解所在的類的包加入到basePackages if (basePackages.isEmpty()) { // 通常我們的主配置類是沒有聲明包掃描的路徑的,所以這里會將主配置類所在的包加到這里面 basePackages.add(ClassUtils.getPackageName(declaringClass)); } // 添加一個ExcludeFilter,跳過聲明該注解的類 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); }
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); // 遍歷所有的包路徑 for (String basePackage : basePackages) { // 獲取該包下面所有符合條件的BeanDefinition,然后遍歷處理,下面會分析 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 通過beanNameGenerator生成beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 這兩個if判斷邏輯比較簡單,就是設置一些Lazy、DependsOn屬性 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 這邊是檢查下有沒有之前定義過這個BeanDefinition if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 將該beanDefinition加入到集合中,并注冊到容器中 beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // 掃描指定包路徑及其子包下面的class文件,將其封裝成Resource對象 // classpath*:com/lwh/springboot/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) { if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } } } } return candidates; }
// 通過之前設置的幾個filter進行過濾 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
// Process any @Import annotations // getImports方法就是去遞歸掃描configClass上面所有的注解,將@Import注解標注的值放入importCandidates中,見下圖 processImports(configClass, sourceClass, getImports(sourceClass), true);
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) { // 依次遍歷判斷類型 // 其中有一個是這個類型,@Import(AutoConfigurationImportSelector.class) // 這個就是自動配置原理,導入xxxAutoConfiguration這些類 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); // 實例化并調用xxxAware的方法并注入相關屬性 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // 它是DeferredImportSelector類型的 if (selector instanceof DeferredImportSelector) { // deferredImportSelectors = new ArrayList<>() // 這邊會將兩個參數封裝下加入到deferredImportSelectors中,后面處理 // 加入到deferredImportSelectors中后,具體的處理是this.deferredImportSelectorHandler.process(); this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 不是的話獲取@Import導入的類名數組 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); // 然后再遞歸處理 processImports(configClass, currentSourceClass, importSourceClasses, false); } } // @Import(AutoConfigurationPackages.Registrar.class),我們的主配置類上面的注解就是這個類型 // 這個是用于導入主配置類所在包及其子包下的BeanDefinition 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 = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 這邊就是將這兩個參數作為key,value放入了一個map中 // this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata); 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)); } } } finally { this.importStack.pop(); } } }
// 這種就是Spring中常用的通過XML形式注入的方式 @SpringBootApplication @ImportResource("test.xml") public class Springboot2Application {
// Process any @ImportResource annotations // 可以使用@ImportResource注解指定xml文件,導入BeanDefinition 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) { // 就是test.xml String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) { // 這邊就是放入到了map中,這邊是先統一存放起來,在步驟五的4)在真正進行導入BeanDefinition this.importedResources.put(importedResource, readerClass); } private final Map<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();
// Process individual @Bean methods // 獲取當前類中的Bean方法 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { // 這邊也是加入到set中,見下面代碼,也是在步驟五的3)中進行處理 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
// 默認方法舉例,主配置類實現這個接口就可以 public interface ConfigurationInterface { @Bean default Pig pig(){ return new Pig(); } }
// Process default methods on interfaces processInterfaces(configClass, sourceClass); // Register default methods on interfaces implemented by the configuration class. // 這邊也是遞歸處理其父接口,判斷父接口中默認方法是不是@Bean方法 private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { for (SourceClass ifc : sourceClass.getInterfaces()) { Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc); for (MethodMetadata methodMetadata : beanMethods) { if (!methodMetadata.isAbstract()) { // 也是在步驟五的3)中進行處理 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } } processInterfaces(configClass, ifc); } }
// 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(); } }
講到這邊步驟四中的大部分方法已經分析完了,但是還有一個重要步驟沒有分析,就是導入xxxAutoConfiguration這些自動配置類部分。下面分析:
// 在解析@Import注解時,我們的AutoConfigurationImportSelector就是DeferredImportSelector類型的,它的意思是延遲導 // 入. 為啥要延遲導入? 因為AutoConfigurationImportSelector是給容器中導入一些默認的組件,如果容器中已經有這種類型 // 的組件了,那么就不再導入. 因此,它是故意先等SpringBoot容器去解析那些用戶自定義的Bean,最后發現沒有才來導入這 // 些默認的組件 if (selector instanceof DeferredImportSelector) { // deferredImportSelectors = new ArrayList<>() // 這邊會將兩個參數封裝下加入到deferredImportSelectors中,后面處理 // 加入到deferredImportSelectors中后,具體的處理是this.deferredImportSelectorHandler.process(); this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); }
// 步驟四就是從這里開始分析的 public void parse(Set<beandefinitionholder> configCandidates) { // 刪除部分代碼,實際執行時這里的configCandidates就一個springBootApplication代表的主配置類 for (BeanDefinitionHolder holder : configCandidates) { // 獲取BeanDefinition BeanDefinition bd = holder.getBeanDefinition(); // 我們的SpringBootApplication會走到這邊,下面先分析這邊 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } // 給容器中導入xxxAutoConfiguration,我們現在分析這邊 this.deferredImportSelectorHandler.process(); }
// 上面方法會走到這里 public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // 下面先分析這個getImports方法 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } }); } } public Iterable<group.entry> getImports() { // 這邊的deferredImports是通過grouping.add(deferredImport)添加進去的 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); } public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { // 下面先分析下這個getAutoConfigurationEntry方法 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); // 加入到這個autoConfigurationEntries中 this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { // 接著依次遍歷放入到這個map中,Map<string, annotationmetadata> entries = new LinkedHashMap<>() this.entries.putIfAbsent(importClassName, annotationMetadata); } }
// 這邊就到了AutoConfigurationImportSelector類的方法中 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { // 判斷是否支持自動配置 if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 這個attributes屬性就是上圖中顯示的,用來過濾自動配置類的 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 下面分析這個方法,這個就是加載容器中的自動配置類 List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去除重復的,方法就是放入set再放入list中 configurations = removeDuplicates(configurations); // 去除掉應該被排除的 Set<string> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 通過filter過濾,下面分析,過濾完發現只有22個了 configurations = filter(configurations, autoConfigurationMetadata); // 發布一個事件,好像沒有做啥關鍵的 fireAutoConfigurationImportEvents(configurations, exclusions); // 將configurations封裝成AutoConfigurationEntry返回 return new AutoConfigurationEntry(configurations, exclusions); } protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { // 判斷有沒有配置這個屬性,沒有的話默認為true, // String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; }
protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // getSpringFactoriesLoaderFactoryClass()返回EnableAutoConfiguration.class,那么這邊就是獲取容器中所有的字段配置類 List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; }
protected List<autoconfigurationimportfilter> getAutoConfigurationImportFilters() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }
// 上面分析的是配置類解析的步驟四 parser.parse(candidates); // 下面來看第五步,步驟四中解析得到的配置類會放在configurationClasses中 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } }
private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { // 判斷是否應該跳過 if (trackedConditionEvaluator.shouldSkip(configClass)) { return; } // 向容器中注入這個配置類所代表的的BeanDefinition if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } // 遍歷這個配置類的所有的Bean方法,注入這些@Bean標注的方法要引入的BeanDefinition for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 處理該配置類上的@ImportResource指定的配置文件,就是Spring中常用的XML配置方式 // 這邊會通過解析該XML文件給容器中注入BeanDefinition,SpringBoot中不推薦這種方式,具體就不往下分析了 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 處理步驟四中D解析@Import注解時獲得的importBeanDefinitionRegistrars,調用其registerBeanDefinitions // 方法給容器中注入BeanDefinition,這在前面講解@Import方式時已經講過了 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); }
以上是“SpringBoot如何解析配置類以及集成第三方配置”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。