• SpringBoot(一)自动配置


    前言

    我以自动配置为SpringBoot的第一篇章,是因为从SpringMvc到SpringBoot,它实现了零配置,并出现了很多默认配置,在进行后面的源码理解时可能会有部分地方不理解,念头不通达,所以先将自动配置这部分给了解清楚,知道它的一个配置是怎么加载的,对后面的学习应该会更流畅一点。

    SpringBoot的自动配置由注解@EnableAutoConfiguration开启,所以我们从这个注解入手,看看它是怎么实现的自动配置,和条件过滤的。

    原理

    外部jar包,或者第三方包他们加载的方式都是一样的,要实现自动配置,需要遵从spring的规范,一般是配置META-INFO/spring.factoriesMETA-INF/spring-autoconfigure-metadata.properties两个文件,从而实现自动配置。

    在Spring章节spring源码篇(六)配置类解析过程,学习了Spring通过配置类,注入bean的方式,如下。

    Spring判断为配置类,有几个注解:@Configuration、@Component、@ComponentScan、@Import、@ImportResource还有@Bean

    所有,Spring常规的注入Bean的方式有(Spring是从beanDefinition去判断的,所有前提要是一个beanDefinition):

    1. @Component/@Configuration

    2. @Component/@Configuration + @Bean

    3. @Component/@Configuration + @CompnentScan

    4. @Component/@Configuration + @Import

    5. @Component/@Configuration + @ImportResource

    那么引入其他外部类的方式也大概可以猜到,可以使用@ComponentScan扫描的方式将外部包扫描进来,而这种方式是应用主动的去扫描,这种主动的方式对于组件整合这种框架就不灵活了,比如你开了一个接口给第三方,然后你在调用接口的时候调用不到,你还要在你的配置上将这个接口添加进来,是不是感觉很违和,就像,我开发一个idea插件,我根据idea的规范做好了,发布了,我还用通知idea官方,让他们将我的插件开通下使用权限,是不是离谱了,没人理你。

    所以我们应该关注的应该时被动的去接受组件,然后扫描,就像组装车子一样,有了spring提供框架,主控芯片,发动机,其他就由我们去组装了。

    Tomcat中,Tomcat初始化应用程序是通过META-INF/services/javax.servlet.ServletContainerInitializer读取到应用实现类,而spring也是一样的,它是通过读取META-INFO/spring.factories这个文件来加载配置的。

    比如mybatis的依赖包

            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    在引入时,会依赖一个

        <dependency>
          <groupId>org.mybatis.spring.bootgroupId>
          <artifactId>mybatis-spring-boot-autoconfigureartifactId>
        dependency>
    
    • 1
    • 2
    • 3
    • 4

    可以看到,它里面配置了两个配置类,这两个就是mybatis能够自动配置的核心image-20221022195751583

    然后我们在来看SpringBoot的自动配置,Spring是会引入下面的一个自动配置依赖,它里面有很多自动配置类,基本上所有的spring-boot的自动配置都在这里,仔细看的话,你会看到aop、jpa、MongoDB、kafka、servlet等等。

     <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-autoconfigureartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    image-20221022231936137

    @EnableAutoConfiguration

    解析Import

    那么SpringBoot是怎么加载外部包,或者说第三方包的呢?

    在启动SpringBoot应用时,都会加上注解@SpringBootApplication,这个注解就是关键

    @SpringBootApplication
    public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    SpringApplication.run(App.class);这个是启动web容器,spring容器,然后就是Spring的那一套,

    我们查看@SpringBootApplication的定义,它里面有一个@EnableAutoConfiguration,这个就是自动配置加载的注解

    image-20221022233013456

    image-20221022233052400

    @EnableAutoConfiguration注解里有这样一个注解@Import({AutoConfigurationImportSelector.class})

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    
    	/**
    	 * {@link Configuration @Configuration}, {@link ImportSelector},
    	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
    	 */
    	Class<?>[] value();
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通过注释我们可以找到,它的value可以设置四种类型class:

    1. 标注了@Configuration的配置类
    2. 实现了ImportSelector接口的类
    3. 实现了ImportBeanDefinitionRegistrar的类
    4. 普通的component

    当Spring启动时会创建ioc容器,并将我们的配置类(@SpringBootApplication标注的启动类)作为配置类加入,它相当于一个Configuration,也是最初的一个配置类,通过这个配置类扫描到其他配置类或bean;在解析这个配置类时,不管有没有imports都会进行的解析

    解析配置类的位置:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

    解析importor的位置:org.springframework.context.annotation.ConfigurationClassParser#processImports

    这一步的作用是将imports里的ImportSelector类找出来,还有将Import注解里的beanDefinition注册器也找出来,只是找出来,并没有做什么处理。

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
    			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
    			boolean checkForCircularImports) {
    
        // 当配置类没有impots时不再进行
    		if (importCandidates.isEmpty()) {
    			return;
    		}
        // 判断是否循环引入
    		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    		}
    		else {
                // 标记正在import的配置类
    			this.importStack.push(configClass);
    			try {
    				for (SourceClass candidate : importCandidates) {
                        
    					if (candidate.isAssignable(ImportSelector.class)) {
    						// 判断import的类是否是ImportSelector子类
                            // @SpringBootApplication里的import注解走的就是这里
    						Class<?> candidateClass = candidate.loadClass();
    						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
    								this.environment, this.resourceLoader, this.registry);
                            // 排除选项
    						Predicate<String> selectorFilter = selector.getExclusionFilter();
    						if (selectorFilter != null) {
    							exclusionFilter = exclusionFilter.or(selectorFilter);
    						}
    						if (selector instanceof DeferredImportSelector) {
                                // ImportSelector还有另一个子类接口DeferredImportSelector,
                                // 这里只做了一个动作:添加importHandler
    							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    						}
    						else {
                                // 普通的imports
    							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
    							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
    							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    						}
    					}
    					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                            // 判断import的类是否是ImportBeanDefinitionRegistrar子类
                            // 这个子类从命名上看就是一个beanDefinition注册器,spring中的所有bean都是先通过beanDefinition注册后,再根据beanDefinition实例化的,所有这里的注册器后面也要添加到已经载入的注册器中
                            // 可以查看:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
                            // 它在parse(配置类)后有这么一句:this.reader.loadBeanDefinitions(configClasses);
                            // 这句就是将这里找出的注册器都添加的到已经加入的spring容器里的BeanDefinitionReader
    						Class<?> candidateClass = candidate.loadClass();
    						ImportBeanDefinitionRegistrar registrar =
    								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
    										this.environment, this.resourceLoader, this.registry);
    						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
    					}
    					else {
    						// import的类不是ImportBeanDefinitionRegistrar,ImportSelector,那么就作为配置类进行载入
    						this.importStack.registerImport(
    								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    						processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
    					}
    				}
    			}
    			catch (BeanDefinitionStoreException ex) {
    				throw ex;
    			}
    			catch (Throwable ex) {
    				throw new BeanDefinitionStoreException(
    						"Failed to process import candidates for configuration class [" +
    						configClass.getMetadata().getClassName() + "]", ex);
    			}
    			finally {
    				this.importStack.pop();
    			}
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    看一下:ParserStrategyUtils.instantiateClass(...),它的实例化不是简单的实例化,还对实例化后的bean做了初始化设置。

    org.springframework.context.annotation.ParserStrategyUtils#instantiateClass

    	static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
    			ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
    
            // 二次校验
    		Assert.notNull(clazz, "Class must not be null");
    		Assert.isAssignable(assignableTo, clazz);
    		if (clazz.isInterface()) {
                // import 的类不能是接口
    			throw new BeanInstantiationException(clazz, "Specified class is an interface");
    		}
    		ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
    				((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
            // 反射实例化,这里AutoConfigurationImportSelector是无参构造
    		T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
            // 这一步就是为这个实例化的AutoConfigurationImportSelector进行遍历初始化,
    		ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
    		return instance;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    从这一步看出,@Imports引入的类,Spring给予它对最大程度的支持:BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware

    	private static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
    			ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {
    
    		if (parserStrategyBean instanceof Aware) {
    			if (parserStrategyBean instanceof BeanClassLoaderAware && classLoader != null) {
    				((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
    			}
    			if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
    				((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
    			}
    			if (parserStrategyBean instanceof EnvironmentAware) {
    				((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
    			}
    			if (parserStrategyBean instanceof ResourceLoaderAware) {
    				((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在上面找到了需要import的类后,执行ImportSelectorHandle

    位置:org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set)

    	public void parse(Set<BeanDefinitionHolder> configCandidates) {
            // 解析配置类
    		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);
    			}
    		}
    
            // importSelectorHandler执行深度import
    		this.deferredImportSelectorHandler.process();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    **总结:**上面的步骤是解析import注解,然后将注解了的类进行解析,包含递归的解析。

    import分三种情况:

    1. 实现了ImportSelector接口,那么这一类的接口属于是需要执行接口方法进行导入的,所以在解析时都会添加到deferredImportSelectorHandler
    2. 实现了ImportBeanDefinitionRegistrar接口的,这个接口属于是beanDefinition的注册接口,spring中的创建bean都有依据beanDefinition来的,所以应该是以统一的步骤为基础,并且在这个解析配置类的过程中,还没有到创建bean的步骤,还在准备阶段,所以后面是要和其他的beanDefinition一起实例化的,所以这里都是放到了configurationClasses里,后面解析完配置类后再从取出来进行合并
    3. 其他情况,普通的配置类,如Configuration component等,它同样是作为配置类放到configurationClasses

    然后下一步(this.deferredImportSelectorHandler.process();),对通过@Import注解拿到的ImportSelector类进行执行,可以说是深度import,这个是我们对它的一个方法功能描述,可能不太准确。

    执行导入类处理器

    这里是执行deferredImportSelectorHandler.process方法,但这里要提一下deferredImportSelectorHandler.handle方法。

    位置:org.springframework.context.annotation.ConfigurationClassParser#processImports

    this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);

    它有一个null的判断,但是类初始化时就new了,不存在null的情况,

    我要说的是整个null的情况是在selector扫描完了,他会执行process的方法(this.deferredImportSelectorHandler.process();),在process方法中,它循环deferredImportSelectors然后执行,并且deferredImportSelectors是非线程安全的,再者解析配置类也是一个循环,所以这个process它不能保证执行过程中会出现add的情况,所以,process会先置空,这样,在handle时,就知道当前的handle集合已经在处理了,不能在往里添加了,那么就直接执行吧;

    而process里重新new了一个list的意义也是一样的,要保证当前循环不被破坏。

    // deferredImportSelectorHandler.handle		
    public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
                // 当要添加时,发现为null,表明这个集合已经在循环处理了,这里可以直接执行
    			if (this.deferredImportSelectors == null) {
    				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
    				handler.register(holder);
    				handler.processGroupImports();
    			}
    			else {
    				this.deferredImportSelectors.add(holder);
    			}
    		}
    
    // deferredImportSelectorHandler.process
    	public void process() {
    			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    			this.deferredImportSelectors = null;
    			try {
                    // 这里执行必然不会是null,这里new List()是为了避免在循环过程中,handle方法又进行添加对象
    				if (deferredImports != null) {
    					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
    					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
    					deferredImports.forEach(handler::register);
    					handler.processGroupImports();
    				}
    			}
    			finally {
    				this.deferredImportSelectors = new ArrayList<>();
    			}
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    那么在接着看process方法

    位置:org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process

    process中deferredImports.forEach(handler::register);添加group,AutoConfigurationImportSelector是方法直接返回的AutoConfigurationGroup.class

    所以这里初始化了两个东西:

    1. group -》 AutoConfigurationGroup.class 自动配置group,group针对两种场景:
      1. 当前jar包 -》DefaultDeferredImportSelectorGroup.class 本篇不分析
      2. 外部jar包 -》 AutoConfigurationGroup.class (本篇分析的重点
    2. deferredImports -》 DeferredImportSelector,@Import.value里DeferredImportSelector的子类,它需要
    		public void register(DeferredImportSelectorHolder deferredImport) {
                // 这里其实是有值的,AutoConfigurationImportSelector它重写了这个方法,并返回了AutoConfigurationGroup.class
    			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
    			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
    					(group != null ? group : deferredImport),
    					key -> new DeferredImportSelectorGrouping(createGroup(group)));
                // 把handle添加到grouping
                // 实际执行是:this.deferredImports.add(deferredImport);
    			grouping.add(deferredImport);
    			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
    					deferredImport.getConfigurationClass());
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    		public void processGroupImports() {
    			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
    				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                    // 这个getImports实际上是通过上一步添加的group.processs执行的
    				grouping.getImports().forEach(entry -> {
                        // 遍历得到的configurationEntry
    					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
    					try {
                            // 递归
    						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
    								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
    								exclusionFilter, false);
    					}
    					catch (BeanDefinitionStoreException ex) {
    						throw ex;
    					}
    					catch (Throwable ex) {
    						throw new BeanDefinitionStoreException(
    								"Failed to process import candidates for configuration class [" +
    										configurationClass.getMetadata().getClassName() + "]", ex);
    					}
    				});
    			}
    		}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    grouping.getImports()实际执行的方法:

    这里group指的是AutoConfigurationImportSelector.AutoConfigurationGroup,是固定的,而deferredImport它是从注解里获取的,并非是固定的,可以把group.process看做一个执行器,deferredImport作为第三方接口实现

    // grouping.getImports()
    public Iterable<Group.Entry> getImports() {
    			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                    // 这里通过AutoConfigurationImportSelector.AutoConfigurationGroup#process执行
    				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
    						deferredImport.getImportSelector());
    			}
        // 这里将导入的类返回
    			return this.group.selectImports();
    		}
    
    // group.process
    /**
     * @param annotationMetadata 配置类的注解信息
     * @param deferredImportSelector 配置类上@Import.value里的类
     */
    @Override
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
       Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
             () -> String.format("Only %s implementations are supported, got %s",
                   AutoConfigurationImportSelector.class.getSimpleName(),
                   deferredImportSelector.getClass().getName()));
        // 这里它会去获取自动配置的entry
       AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
             .getAutoConfigurationEntry(annotationMetadata);
       this.autoConfigurationEntries.add(autoConfigurationEntry);
       for (String importClassName : autoConfigurationEntry.getConfigurations()) {
          this.entries.putIfAbsent(importClassName, annotationMetadata);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    这里是把从spring.fatories文件读取到的类(List)构建成AutoConfigurationEntry,可以看做一个配置类对象,后面还有进行解析

    	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    		if (!isEnabled(annotationMetadata)) {
    			return EMPTY_ENTRY;
    		}
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
            // 获取符合条件的配置类,这里是字符串
    		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    		configurations = removeDuplicates(configurations);
            // 过滤排除的类
    		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    		checkExcludedClasses(configurations, exclusions);
    		configurations.removeAll(exclusions);
            // 加载配置过滤
            // 读取spring.factories中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的3个条件处理类,作为导入配置类的过滤处理器(import filter)
            // 然后读取META-INF/spring-autoconfigure-metadata.properties下的键值(配置类的条件,格式className.condition)
            // 这里这样处理,是因为在这一步并没有加载class,还知识className,通过spring-autoconfigure-metadata.properties配置,将条件写进到里面,然后进行过滤,下面还会提到,这里只是说明一下
            
    		configurations = getConfigurationClassFilter().filter(configurations);
    		fireAutoConfigurationImportEvents(configurations, exclusions);
    		return new AutoConfigurationEntry(configurations, exclusions);
    	}
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里是它获取 List configurations的逻辑,除了读取文件那一部分没有,从这段已经可以看出它是通过SpringFactoriesLoader.loadFactoryNames根据EnableAutoConfiguration 全类名加载的value,这个方法的底层是直接读取META-INF/spring.factories

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            // getSpringFactoriesLoaderFactoryClass() = () -> return EnableAutoConfiguration.class;
            // 所以这里它是通过 EnableAutoConfiguration 全类名获取对应 META-INF/spring.factories 下的value
    		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    				getBeanClassLoader());
    		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    				+ "are using a custom packaging, make sure that file is correct.");
    		return configurations;
    	}
    
    // 上面一步的:SpringFactoriesLoader.loadFactoryNames方法实现
    
        public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
            ClassLoader classLoaderToUse = classLoader;
            if (classLoader == null) {
                classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
            }
    
            String factoryTypeName = factoryType.getName();
            return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    所以SpringBoot的自动配置是读取这个键的值

    image-20221023202016235

    **总结:**这一步是执行DeferredImportSelector的类,在@Import中有引入其子类的,这里都会执行,@EnableAutoConfiguration注解里的是AutoConfigurationImportSelector,所以会执行getAutoConfigurationEntry方法,从而读取

    META-INF/spring.factories下的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的键值,这时加载到的是List,然后再读取配置类条件过滤处理器进行过滤,之后构建成entry放到autoConfigurationEntries,之后再遍历从spring.factories里得到的autoConfigurationEntries递归走processImports从解析import开始。

    SpringBootCondition

    在自动配置中,还有一个比较重要的一类注解Conditional,也是自动配置的一个功能,可以根据环境条件进行bean的注入。

    比如:

    @ConditionalOnBean

    @ConditionalOnMissingBean

    @ConditionalOnClass

    而在SpringBoot中,SpringBootCondition是所有条件处理类基础,其他都是通过实现它的getMatchOutcome接口完成判定就行。

    比如@ConditionalOnBean,它上面还标注了@Conditional(OnBeanCondition.class),那么它的处理类就是OnBeanCondition

    image-20221026231312435

    这里我还要提一个点,因为上面我没有详细说明;

    上面@Import注解会读取META-INF/spring.factories下的配置类,一共会读取两个key的键值:

    EnableAutoConfiguration -> 这个是import的类

    AutoConfigurationImportFilter -> 这个是Condition条件处理类的实现类,用来处理import的类

    而这时导入的都是String类型的,还没有读取class,因为存在像这样的ConditionalOnClass注解,所以加载class前会有一次过滤,过滤条件就配置就保存在META-INF/spring-autoconfigure-metadata.properties;它的key=class类全名.Condition注解名称,表示配置类标注了哪些条件注解,可以翻看spring和mybatis的包,都会找到的。

    image-20221026230342257

    image-20221026225808585

    判定

    org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

    该类是解析配置类之前都会去调用的一个验证条件验证类,判断该类是否被加载,都是调用shouldSkip

    位置:org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

    ConfigurationPhase有两个值:

    1. PARSE_CONFIGURATION -》 表示解析配置该时机正处于解析配置阶段,shouldSkip判断失败,不会加载class
    2. REGISTER_BEAN -》 表示该时机正处于注册bean阶段,shouldSkipt判断失败,不会注册bean
    /**
     * @param metadata 注解元数据信息
     * @param phase 执行方法的阶段,时机
    */
    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        // 是否有条件注解
    		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
    			return false;
    		}
    
    		if (phase == null) {
    			if (metadata instanceof AnnotationMetadata &&
    					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
    				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
    			}
    			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    		}
    
    		List<Condition> conditions = new ArrayList<>();
        // 从条件注解中获取到条件处理类,如@ConditionalOnMissingBean,拿到的就是这个注解上面的@Conditional(OnBeanCondition.class)的OnBeanCondition
    		for (String[] conditionClasses : getConditionClasses(metadata)) {
    			for (String conditionClass : conditionClasses) {
    				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
    				conditions.add(condition);
    			}
    		}
    
    		AnnotationAwareOrderComparator.sort(conditions);
    
    		for (Condition condition : conditions) {
    			ConfigurationPhase requiredPhase = null;
    			if (condition instanceof ConfigurationCondition) {
    				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
    			}
                // 这里就是条件判断,看matches
                // 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
    			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    				return true;
    			}
    		}
    
    		return false;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    对于下面这个判断来说,所以配置类的条件校验都实现于ConfigurationCondition,但对于不同的配置,解析加载的时机不同,也会有所差别。

    // 这里就是条件判断,看matches
                // 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
    			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    				return true;
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    OnBeanCondition为例,它是需要REGISTER_BEAN阶段才进行判断,因为,onBeanConditional这个条件针对bean对象,那么他需要准备好class,当扫描到一个配置类,它在missingBean,或onBean时,才能直接取出。

    OnBeanCondition实际的匹配方法:

    @Override
    	protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
    			AutoConfigurationMetadata autoConfigurationMetadata) {
            // 定义结果数组
    		ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
    		for (int i = 0; i < outcomes.length; i++) {
    			String autoConfigurationClass = autoConfigurationClasses[i];
    			if (autoConfigurationClass != null) {
                    // 这里是获取当前配置类,在条件ConditionalOnBean下所需要的处理类
                    // 这里get的是spring-autoconfigure-metadata.properties读取到的key value
    				Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
                    // 查找当前需要的bean类型,返回返回结果信息
    				outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
    				if (outcomes[i] == null) {
                        // 这里也是一样,查找需要的类型,返回结果信息
    					Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
    							"ConditionalOnSingleCandidate");
    					outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
    				}
    			}
    		}
    		return outcomes;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    	
    /**
     * @param requiredBeanTypes 需要查找的bean的类全名
     * @param annotation 条件注解,这里因为是ConditionalOnBean代码,所以这里是ConditionalOnBean
     */
    private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
            // 这里是通过需要的类型,去Class.forName反射加载,能加载就将当前的类名添加到missing里
    		List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader());
    		if (!missing.isEmpty()) {
                // 构建条件检索结果
    			ConditionMessage message = ConditionMessage.forCondition(annotation)
    					.didNotFind("required type", "required types").items(Style.QUOTE, missing);
    			return ConditionOutcome.noMatch(message);
    		}
    		return null;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里ClassNameFilter.MISSING是一个方法

    MISSING {
    
    			@Override
    			public boolean matches(String className, ClassLoader classLoader) {
                    // 里面其实是通过反射`Class.forName`加载,加载失败那就是没有,返回false
    				return !isPresent(className, classLoader);
    			}
    
    		};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后返回org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match

    	@Override
    	public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
    		ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
            // 这里就是上面的执行方法,查找需要的类,返回返回结果
    		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
    		boolean[] match = new boolean[outcomes.length];
    		for (int i = 0; i < outcomes.length; i++) {
    			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
    			if (!match[i] && outcomes[i] != null) {
                    // 日志输出
    				logOutcome(autoConfigurationClasses[i], outcomes[i]);
    				if (report != null) {
                        // 日志记录
    					report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
    				}
    			}
    		}
    		return match;
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    主方法是:org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)

    它有很多子类,其实现都有不同,但匹配的逻辑差不多。

    总结

    @Import导入支持

    1. 标注了@Configuration的配置类
    2. 实现了ImportSelector接口的类
    3. 实现了ImportBeanDefinitionRegistrar的类
    4. 普通的component

    SpringBoot自动配置中使用@Import(AutoConfigurationImportSelector.class),它通过AutoConfigurationImportSelector读取META-INF/spring.factories下的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的键值,加载到配置类列表,然后再读取key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的条件过滤处理器,和META-INF/spring-autoconfigure-metadata.properties文件的配置类条件配置,进行过滤,得到配置类的entry,然后在遍历得到的entry,再走一遍import的解析过程(递归)。

    所以我们要实现我们自定义的spring-boot-starter,就要写一个META-INF/spring.factories,就像mybatis这样

    image-20221022195751583

    这一过程是在解析配置类的时候进行,再解析完配置类后,会把解析的配置类注册到ConfigurationClassBeanDefinitionReader中,待后面进行实例化。

    配置类条件处理器,我们也可以自己定义,继承SpringBootCondition就行,再自定义一个注解,这里偷懒了,没有写demo,以后有时间写一个看看。

  • 相关阅读:
    高级前端手写面试题
    Pytest----当fixture重名时如何调用
    go使用benchmark分析json库性能
    【MySQL】Mysql事务以及权限管理
    笔记本屏幕忽亮忽暗解决方法大全,总有一款适合你
    TMP451
    从大龄程序员现状聊聊出路
    算法--冒泡排序
    【pygame】01 pygame制作游戏的最小系统
    php高校师生交流作业信息管理系统mysql
  • 原文地址:https://blog.csdn.net/qq_28911061/article/details/127544104