IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot(一)自动配置 -> 正文阅读

[Java知识库]SpringBoot(一)自动配置

前言

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

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

原理

在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.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

在引入时,会依赖一个

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>

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

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

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

image-20221022231936137

@EnableAutoConfiguration

解析Import

那么SpringBoot是怎么加载的呢?

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

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}

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

}

通过注释我们可以找到,它的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();
			}
		}
	}

看一下: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;
	}

从这一步看出,@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);
			}
		}
	}

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

位置:org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

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

**总结:**上面的步骤是解析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<>();
			}
		}
	}

那么在接着看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());
		}
		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);
					}
				});
			}
		}

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

这里是把从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);
	}



这里是它获取 List<String> 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());
    }

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

image-20221023202016235

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

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

SpringBootCondition

在自动配置中,还有一个比较重要的一类注解Conditional

比如:

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

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

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

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;
	}
	
/**
 * @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;
	}

这里ClassNameFilter.MISSING是一个方法

MISSING {

			@Override
			public boolean matches(String className, ClassLoader classLoader) {
                // 里面其实是通过反射`Class.forName`加载,加载失败那就是没有,返回false
				return !isPresent(className, classLoader);
			}

		};

然后返回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;
	}

主方法是: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,以后有时间写一个看看。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:46  更:2022-10-31 11:42:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:53:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码