iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >SpringBoot如何解析配置类以及集成第三方配置
  • 590
分享到

SpringBoot如何解析配置类以及集成第三方配置

2023-06-03 15:06:48 590人浏览 薄情痞子
摘要

小编给大家分享一下SpringBoot如何解析配置类以及集成第三方配置,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!springBoot作为Java领域非常流行的

小编给大家分享一下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方法中会进行配置类的解析@SpringBootApplicationpublic 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 {

1、@SpringBootConfiguration注解

// 点进去发现它其实就是一个@Configuration注解,SpringBoot解析到就会知道这是一个配置类,会给容器中引入一些bean// 一个被@Configuration标注的类,相当于一个applicationContext.xml文件// @Configuration点进去发现其实就是一个@Component注解@Configurationpublic @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 {}

@Componentpublic 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);    }}

@Componentpublic 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解析自动配置类会用到的一些知识点,下面我们来看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,它是BeanFactoryPostProcessorif (!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方法。

1、配置类解析流程概述

SpringBoot如何解析配置类以及集成第三方配置

@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());  }

2、检测是否是配置类

在配置类解析流程图中,第二步,会获取容器中已经注册的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());  }

3、步骤四解析

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 -&gt; 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 -&gt; processing is complete      return null;  }[object Object]
(1)处理内部类
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();                  }               }           }      }  }
(2)处理@PropertySource注解
@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);      }  }
(3)处理@ComponentScan注解
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   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;  }
(4)处理@Import注解
// 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();        }     }  }
(5)处理@ImportSource注解
// 这种就是Spring中常用的通过XML形式注入的方式@SpringBootApplication  @ImportResource("test.xml")  public class Springboot2Application {
// Process any @ImportResource annotations// 可以使用@ImportResource注解指定xml文件,导入BeanDefinitionAnnotationAttributes 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<>();
(6)处理@Bean标注的方法
// 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<>();
(7)处理默认方法
// 默认方法举例,主配置类实现这个接口就可以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);       }  }
(8)递归处理父类
// 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);  }

4、步骤五解析

// 上面分析的是配置类解析的步骤四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如何解析配置类以及集成第三方配置”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: SpringBoot如何解析配置类以及集成第三方配置

本文链接: https://www.lsjlt.com/news/234722.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SpringBoot如何解析配置类以及集成第三方配置
    小编给大家分享一下SpringBoot如何解析配置类以及集成第三方配置,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!SpringBoot作为Java领域非常流行的...
    99+
    2023-06-03
  • Java SpringBoot详解集成以及配置Swagger流程
    一、swagge简介 前后端分离: 后端︰后端控制层,服务层,数据访问层【后端团队】 前端:前端控制层,视图层【前端团队】 前后端通过API进行交互 前后端相对独立且松耦合 产生问题...
    99+
    2024-04-02
  • SpringBoot如何集成Apollo配置中心
    这篇文章将为大家详细讲解有关SpringBoot如何集成Apollo配置中心,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。系统环境SpringBoot 版本:2.1.8.RELEASEApollo 版本:...
    99+
    2023-06-02
  • Jspxcms第三方登录如何配置
    本文小编为大家详细介绍“Jspxcms第三方登录如何配置”,内容详细,步骤清晰,细节处理妥当,希望这篇“Jspxcms第三方登录如何配置”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。第三方登录支持新浪微博、腾讯Q...
    99+
    2023-06-26
  • MyEclipse6.0集成SVN及配置详解
    要在MyEclipse 6.0中集成SVN,并进行配置,可以按照以下步骤进行操作:1. 安装SVN插件打开MyEclipse 6.0...
    99+
    2023-09-12
    MyEclipse
  • SpringBoot整合JPA方法及配置解析
    目录JPA与Hibernate及Spring Data JPA的关系整合JPApom.xmlapplication.ymlPerson实体类PersonRepository接口Tes...
    99+
    2023-05-20
    Springboot整合JPA SpringBoot整合Java Persistence API
  • SpringBoot集成slf4j日志配置的方法
    目录前言 1、slf4j概述 2、pom.xml的日志依赖 3、application.yml的日志配置 4、logback.xml配置文件定义 5、logback.xml配置文件解...
    99+
    2024-04-02
  • Springboot集成Swagger2以及常见配置的过程是怎样的
    本篇文章给大家分享的是有关Springboot集成Swagger2以及常见配置的过程是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。&n...
    99+
    2024-04-02
  • SpringBoot HikariCP配置项及源码解析
    目录前言为什么HikariCP性能高常用配置项autoCommitconnectionTimeoutidleTimeoutmaxLifetimeconnectionTestQuery...
    99+
    2023-02-02
    SpringBoot HikariCP配置 SpringBoot HikariCP
  • SpringBoot多环境配置及配置文件分类实例详解
    目录二、配置文件分类2.1 代码演示2.1.1 环境准备2.1.2 验证1级和2级的优先级2.1.3 验证2级和4级的优先级一、多环境配置 在工作中,对于开发环境、测试环境、生产环境...
    99+
    2022-11-13
    SpringBoot多环境配置 SpringBoot配置文件
  • SpringBoot集成Kafka配置工具类的详细代码
    目录1、单播模式,只有一个消费者组2、广播模式,多个消费者组spring-kafka 是基于 java版的 kafka client与spring的集成,提供了 KafkaTempl...
    99+
    2024-04-02
  • SpringBoot集成短信和邮件的配置方法
    本文小编为大家详细介绍“SpringBoot集成短信和邮件的配置方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringBoot集成短信和邮件的配置方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。准备工...
    99+
    2023-06-30
  • Swagger2配置方式以及如何解决404报错
    Swagger2配置方式以及如何解决404报错,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Swagger2配置在spring boot项目中配置Swagger2,配置好了...
    99+
    2023-06-25
  • 如何解析PHP及配置文件
    这篇文章将为大家详细讲解有关如何解析PHP及配置文件,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。PHP是什么PHP是HypertextPreprocessor超文本预处理器的缩写,是一种广...
    99+
    2023-06-04
  • Quartz集群原理以及配置应用的方法详解
    Quartz是一个开源的任务调度框架,用于在Java应用程序中实现定时任务的调度和执行。Quartz集群是指多个Quartz实例组成...
    99+
    2023-08-15
    Quartz
  • 如何在 Linux 上安装和配置 Apache 和 Django,以及与 Java 集成?
    Apache 是一款广泛使用的 Web 服务器软件,在 Linux 上安装和配置 Apache 并与 Django 集成是 Web 开发的基础工作之一。本文将介绍如何在 Linux 上安装和配置 Apache 和 Django,并与 Jav...
    99+
    2023-08-23
    linux apache django
  • 以示例讲解Clickhouse Docker集群部署以及配置
    目录写在前面环境部署Zookeeper集群部署Clickhouse集群部署1.临时镜像拷贝出配置  2.修改config.xml配置3.拷贝到其他文件夹4.分发到其他服务器配置集群1...
    99+
    2024-04-02
  • SpringBoot 自动配置原理及源码解析
    初始化一个Springboot项目,在主启动类会有这么一个注解:@SpringBootApplication,自动装配的秘密全在主启动类这个注解里面了 点进去一层会发现有三个子注解组...
    99+
    2024-04-02
  • ldap集成配置如何启用ssl
    要启用LDAP集成配置的SSL,您需要执行以下步骤: 获取或生成SSL证书和密钥文件。您可以从证书颁发机构(CA)购买证书,或者...
    99+
    2023-10-24
    ldap ssl
  • SpringBoot集成mqtt的多模块项目配置详解
    目录前言开发工具及系统环境项目路径配置过程1. 搭建父项目2. 搭建子项目3. 配置各个模块4. 配置MQTT模块前言 近期为了准备毕设,准备使用SpringBoot搭建mqtt后端...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作