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核心运行原理

2.1 核心运行原理

springboot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories中注册的各种Autoconfiguration自动配置类,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件时,则实例化该AutoConfiguration类中定义的Bean,并把这些Bean注册到Spring容器中,就可以完成依赖框架的自动配置。

2.2 @EnableAutoConfiguration

2.2.1 入口类和@SpringBootApplication

入口类

以下为springboot应用的常规入口类,形式上非常简单,类上面仅使用了@SpringBootApplication注解,在main方法中,指定入口类作为SpringApplication#run方法的运行参数,就可以启动springboot应用

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

@SpringBootApplication

该注解是springboot应用的核心注解,它里面组合了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

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

	// @Alias注解:该注解用于桥接到其它注解,该注解的属性中制定了所桥接的注解类
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};  // 指定排除的自动配置类

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};  // 指定排除的自动配置类类名
	
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {}; // 指定扫描的基础包

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {}; // 指定扫描的基础类

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true; // 是否生成配置类的代理类, 代理@Bean方法强制从spring容器中获取

}

我们刚学springboot的时候,就被“告知”:入口类所在的包及其子包中的组件将会被扫描。这也是要将入口类放在顶级package下的原因(如果放在比较低的层级,那么上级包的类或同级所在包的类无法扫描到),那么为什么呢?因为@SpringBootApplication注解上有个@ComponentScan注解,默认情况下,我们并没有指定任何扫描的基础包,而在ComponentScanAnnotationParser处理这个@ComponentScan注解的时候,获取到如果没有设置任何扫描的信息,那么就获取注解所标注的类所在的包作为扫描的基础包,因而基础包及其下面定义的组件都会被扫描到。我们可以,@SpringBootApplication(scanBasePackages = “com.zzhua.test”)桥接到@ComponentScan注解中的值,那么就只会com.将zzhua.test作为基础包扫描。

2.2.2 @EnableAutoConfiguration注解解析

@EnableAutoConfiguration

该注解是由@SpringBootApplication注解组合而引入的,用于开启自动配置,它会根据类路径上是否有引入指定的包中的类,尝试猜测并配置项目中可能需要的bean。所以也可以使用该注解提供的exclude等方法,排除某些自动配置。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 被该注解标记的类所在的包会作为自动配置基础包(比如扫描一些Entity啥的)
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; // 是否开启自动配置(通过env设置)

	Class<?>[] exclude() default {}; // 排除指定的类

	String[] excludeName() default {}; // 排除指定名字的类

}

2.3 AutoConfigurationImportSelector

该类在@EnableAutoConfiguration注解中被@Import(AutoConfigurationImportSelector.class)组合而引入

2.3.1 @Import

该注解用于向spring容器中导入组件,在源码中很多@EnableXXX也是基于该注解实现的

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	// 用来导入:
	// 	1. @Configuration标注的类
	//  2. 实现了ImportSelector或DeferedImportSelector接口的子类
	//  3. 实现了ImportBeanDefinitionRegistrar接口的子类
	//  4. 常规普通类(作为组件)
	Class<?>[] value();

}

2.3.2 ImportSelector&DeferedImportSelector接口

public interface ImportSelector {

	// 参数:包含了被@Import注解标注的类上的注解信息
	// 返回值:返回的数组将都会作为spring容器的配置类,用于引入相关组件
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

如果ImportSelector的实现类,还实现了EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware接口,那么这些接口的回调都会在selectImports方法执行之前被回调,这样就可以拿到相应的对象了。

区别

@DeferedImportSelector会在所有的@Configuration类加载完成之后再加载返回的配置类,而ImportSelector在加载完@Configuration类之前先去加载返回的配置类,并且DeferedImportSelector的加载顺序可以通过@Order注解或实现Order接口来指定,并且它还可以了新的getImportGroup()来跨DeferedImportSelector实现自定义@Configuration的加载顺序。

2.3.3 AutoConfigurationImportSelector功能概述

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		// 获取env配置的spring.boot.enableautoconfiguration值,如果未配置,默认为true
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS; // 如果关闭默认配置,直接返回空数组,即所有自动配置类都不会自动配置了
		}
		// 加载类路径下META-INF/spring-autoconfigure-metadata.properties文件的所有数据
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
				
		// 【核心方法:获取所有的自动配置类】
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
				
		// 返回所有自动配置类的数组
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

AutoConfigurationMetadataLoader#loadMetadata

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			// 获取所有的文件
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			while (urls.hasMoreElements()) {
				// 遍历文件,加载所有的属性
				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
			}
			return new PropertiesAutoConfigurationMetadata(properties);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
}
# 多个值,使用逗号分隔(分隔的作用在后面解析中处理)
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate

加载元数据配置,通过加载类路径下,指定路径:META-INF/spring-autoconfigure-metadata.properties文件。加载此数据是为了后续过滤自动配置使用。SpringBoot使用一个Annotation的处理器来收集自动加载的条件,这些条件就可以配置在这个文件当中。Springboot会将收集好的自动配置类,根据此配置信息,使用加载的过滤器(默认配置了3种:OnBeanCondition、OnClassCondition、OnWebApplicationCondition,它们都继承了FilteringSpringBootCondition),再进行一次过滤(跟在自动配置类上添加这些@ConditionalOnClass作用是一样的)。目的就是为了减少@Configuratin类的数量,从而减少初始化bean的耗时。

getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
		AnnotationMetadata annotationMetadata) {
	// 获取env中设置的spring.boot.enableautoconfiguration值, 是否开启默认自动配置
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	
	// 获取到@Import注解所标注的类上的所有注解,中的@EnableAutoConfiguratin注解的信息
	// AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(EnableAutoConfiguration.class.getName(), true))
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	
	// 使用SpringFactoriesLoader加载META-INFO/spring.factories文件中的@EnableAutoConfiguratin注解所对应的值(即自动配置类)
	// 具体的加载细节:https://www.processon.com/view/link/61f0c54ee0b34d616d516e16
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

	// 使用LinkedHashset来对所有的自动配置类来个去重操作
	configurations = removeDuplicates(configurations);

	// 根据@EanbleAutoConfiguratin注解的exclude和excludeName以及env里的spring.autoconfigure.exclude来获取需要排除的自动配置类
	// (初步过滤)
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);

	// 你如果要排除某个自动配置类, 但是这个自动配置类又没有被加载到, 就会抛异常
	checkExcludedClasses(configurations, exclusions);

	// 移除 要排除 的自动配置类
	configurations.removeAll(exclusions);

	// 检查配置类的注解是否符合spring.factories文件中AutoConfigurationImportFilter指定的注解检查条件, 如果不满足将会被移除掉
	// (再次过滤)
	configurations = filter(configurations, autoConfigurationMetadata);

	// 获取到spring.factories文件中所配置的AutoConfigurationImportListener监听器, 组装AutoConfigurationImportEvent, 
	// 并回调监听器的onAutoConfigurationImportEvent方法以广播事件
	fireAutoConfigurationImportEvents(configurations, exclusions);

	// 简单封装
	return new AutoConfigurationEntry(configurations, exclusions);
}

isEnabled(AnnotationMetadata metadata)

protected boolean isEnabled(AnnotationMetadata metadata) {
	// 如果是 AutoConfigurationImportSelector类
	if (getClass() == AutoConfigurationImportSelector.class) {
		// 固定的值"spring.boot.enableautoconfiguration"
		return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
	}
	// 非AutoConfigurationImportSelector类,全都返回true
	return true;
}

getAttributes(AnnotationMetadata metadata)

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {

	// 找的注解就是 @EnableAutoConfiguration
	String name = getAnnotationClass().getName();

	// 提取@EnableAutoConfiguration注解的属性值, 封装到AnnotationAttributes(继承自LinkedHashMap)
	AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
	
	Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
			+ " annotated with " + ClassUtils.getShortName(name) + "?");
	return attributes;
}

protected Class<?> getAnnotationClass() {
	// 找的注解就是 EnableAutoConfiguration
	return EnableAutoConfiguration.class;
}

getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

	// 使用SpringFactoriesLoader加载META-INF/spring.factories中@EnableAutoConfiguration注解全类名对应的值
	// 具体的加载细节:https://www.processon.com/view/link/61f0c54ee0b34d616d516e16
	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;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
	// 找的注解就是 @EnableAutoConfiguration
	return EnableAutoConfiguration.class;
}

removeDuplicates(List list)

protected final <T> List<T> removeDuplicates(List<T> list) {
	// 仅仅用LinkedHashSet去重
	return new ArrayList<>(new LinkedHashSet<>(list));
}

getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes)

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	Set<String> excluded = new LinkedHashSet<>();

	// 那其实我们使用@SpringBootApplication注解, 并没有碰触到@EnableAutoConfiguration呀
	// 那是怎么修改到exclude和excludeName的属性值呢?
	// 前面提到了@SpringBootApplication注解的这两个属性使用了@AliasFor将属性桥接到@EnableAutoConfiguration了

	// 获取@EnableAutoConfiguration注解的exclude属性
	excluded.addAll(asList(attributes, "exclude"));

	// 获取@EnableAutoConfiguration注解的excludeName属性
	excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));

	// 从env中获取spring.autoconfigure.exclude对应要排除的自动配置类
	excluded.addAll(getExcludeAutoConfigurationsProperty());
	return excluded;
}

private List<String> getExcludeAutoConfigurationsProperty() {
	if (getEnvironment() instanceof ConfigurableEnvironment) {

		// 绑定器, 拿到env中的propertySource
		// PropertyPlaceholderHelper用来解析"${x:y}"占位符
		// ApplicationConversionService(ConversionService的实现)用来类型转换
		Binder binder = Binder.get(getEnvironment());
		
		// 获取固定的值:spring.autoconfigure.exclude
		return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
				.orElse(Collections.emptyList());
	}
	String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
	return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}

checkExcludedClasses(List configurations, Set exclusions)

private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
	List<String> invalidExcludes = new ArrayList<>(exclusions.size());
	
	// 遍历要排除的自动配置类
	for (String exclusion : exclusions) {
		// 如果这个类存在, 但是根本就不在自动配置类集合中,就会收集异常
		if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
			invalidExcludes.add(exclusion);
		}
	}

	// 将收集的异常抛出去
	if (!invalidExcludes.isEmpty()) {
		handleInvalidExcludes(invalidExcludes);
	}
}

filter(List configurations, AutoConfigurationMetadata autoConfigurationMetadata)

private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
	long startTime = System.nanoTime();

	// 候选的自动配置类
	String[] candidates = StringUtils.toStringArray(configurations);

	// 记录每个自动配置类是否要跳过
	boolean[] skip = new boolean[candidates.length];

	// 是否要全部跳过
	boolean skipped = false;

	// getAutoConfigurationImportFilters使用SpringFactoriesLoader
	// 加载spring.factories中AutoConfigurationImportFilter全类名所对应的过滤器,并实例化
	// autoconfigure包中默认提供了3个filter,默认是OnBeanCondition、OnClassCondition、OnWebApplicationCondition
	// 它们都继承自FilteringSpringBootCondition抽象类(可以认为这个抽象类的实现就是用来过滤springboot的自动配置类的)
	for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { // 遍历过滤器
	
		// 回调Aware接口, 设置BeanClassLoader、BeanFactory、Environment、ResourceLoader
		invokeAwareMethods(filter);

		// 使用过滤器对每个候选的自动配置类, 使用boolean数组记录是否匹配
		boolean[] match = filter.match(candidates, autoConfigurationMetadata);
		for (int i = 0; i < match.length; i++) {
			if (!match[i]) { // 如果有不匹配的
				skip[i] = true;         // 标记要跳过此候选的自动配置类
				candidates[i] = null;   // 候选的自动配置类置为null
				skipped = true;         // 标记有要跳过的自动配置类
			}
		}
	}
	
	// 如果没有要跳过的自动配置类, 就返回全部的自动配置类
	if (!skipped) {
		return configurations;
	}

	// 最终要返回的 自动配置类 集合
	List<String> result = new ArrayList<>(candidates.length);
	for (int i = 0; i < candidates.length; i++) {
		if (!skip[i]) { // 忽略要跳过的自动配置类
			result.add(candidates[i]);
		}
	}
	if (logger.isTraceEnabled()) {
		int numberFiltered = configurations.size() - result.size();
		logger.trace("Filtered " + numberFiltered + " auto configuration class in "
				+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
	}
	return new ArrayList<>(result);
}
FilteringSpringBootCondition
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {

	// 只是做一个报告, 无关紧要
	ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
	
	// 抽象模板方法, 交给具体子类实现, 这里以OnClassCondition为例继续看看
	ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
	
	// 与自动配置类等长的boolean数组
	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;
}


OnClassCondition
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {

	@Override
	protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
		// Split the work and perform half in a background thread if more than one
		// processor is available. Using a single additional thread seems to offer the
		// best performance. More threads make things worse.
		if (Runtime.getRuntime().availableProcessors() > 1) {
			return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
		}
		else {
			// 假设处理器只有一个, 走这边
			// 自动配置类 和 由 spring-autoconfigure-metadata.properties文件加载的属性值AutoConfigurationMetadata 
			// 封装到了内部类的OutcomesResolver 解析器
			OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
					autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
			// 那剩下就要看这个解析器的resolveOutcomes方法了
			return outcomesResolver.resolveOutcomes();
		}
	}
}


private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
		AutoConfigurationMetadata autoConfigurationMetadata) {
	
	ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
	
	for (int i = start; i < end; i++) {
	
		// 遍历每个自动配置类
		String autoConfigurationClass = autoConfigurationClasses[i];
		
		// 忽略null
		if (autoConfigurationClass != null) {
			// 固定格式是: 自动配置类全类名.ConditionalOnClass
			// 以上面的值, 到pring-autoconfigure-metadata.properties文件中找配置的值
			// 比如说:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,\
			//               org.springframework.amqp.rabbit.core.RabbitTemplate
			String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");

			// 如果有配置这个值的话
			// (所以如果要给自动配置类生效添加条件, 我们也可以往这个文件中写入对应的要求, 也可以自定义AutoConfigurationImportFilter的实现)
			if (candidates != null) {
			
				// 把配置的值传进去, 做条件判断
				outcomes[i - start] = getOutcome(candidates);
			}
		}
	}
	return outcomes;
}

private ConditionOutcome getOutcome(String candidates) {
	try {
		// 语法是使用逗号隔开多个值, 如果没有逗号, 只需要判断一个就行了
		if (!candidates.contains(",")) {
			return getOutcome(candidates, this.beanClassLoader);
		}
		// 如果有逗号, 以逗号分隔, 挨个判断, 第一个不为null就表示已经有结果了, 立即返回
		for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
			ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
			if (outcome != null) {
				return outcome;
			}
		}
	}
	catch (Exception ex) {
		// We'll get another chance later
	}
	return null;
}

private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
	// 不存在className的类, 就返回true
	if (ClassNameFilter.MISSING.matches(className, classLoader)) {
		// 因为条件是onClassCondition, 所以这里是不匹配的
		return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
				.didNotFind("required class").items(Style.QUOTE, className));
	}
	// null表示接着判断下一个
	return null;
}

fireAutoConfigurationImportEvents(List configurations, Set exclusions)

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {

	// SpringFactoriesLoader加载AutoConfigurationImportListener全类名对应的所有实例
	List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();

	// 如果AutoConfigurationImportListener集合不为空
	if (!listeners.isEmpty()) {
	
		// 封装事件对象
		AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);

		// 遍历所有的监听器
		for (AutoConfigurationImportListener listener : listeners) {
		
			// 回调Aware接口, 设置BeanClassLoader、BeanFactory、Environment、ResourceLoader
			invokeAwareMethods(listener);
	
			// 通知每个监听器
			listener.onAutoConfigurationImportEvent(event);
		}
	}
}

2.4 @Conditional注解

2.4.1 @Conditional

spring4.0中定义的注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	// 所有的Condition都满足时, 对应标注的组件才会生效
	Class<? extends Condition>[] value();

}

Condition接口

@FunctionalInterface
public interface Condition {

	// 传入的2个参数, 作为条件判断的基础依据
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

ConditionContext

public interface ConditionContext {

	// bean注册中心, 可以获取容器中是否定义了相关的bean
	BeanDefinitionRegistry getRegistry();

    // bean工厂, 可以获取容器中存在的bean
	@Nullable
	ConfigurableListableBeanFactory getBeanFactory();

	// env环境配置, 可以获取配置信息
	Environment getEnvironment();
	
	// 资源加载器
	ResourceLoader getResourceLoader();

	// 类加载器
	@Nullable
	ClassLoader getClassLoader();

}

AnnotatedTypeMetadata

可以获取所标注的 或者 方法 上的所有注解信息,而无须加载对应的类(asm实现)

public interface AnnotatedTypeMetadata {

	// 获取所有的注解
	MergedAnnotations getAnnotations();

	// 是否标注了指定注解名称的注解
	boolean isAnnotated(String annotationName);
	
	// 获取指定注解的所有数据
	Map<String, Object> getAnnotationAttributes(String annotationName);
	
	Map<String, Object> getAnnotationAttributes(String annotationName,
	                                            boolean classValuesAsString);
	
	MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
}

例如:

@Component("s1")
@Service("s2")
public class Student {

    static {
        System.out.println("static executing...");
    }

    public Student() {
        System.out.println("student cons...");
    }
}

public class TestApp {

    public static void main(String[] args) throws IOException {

        SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        MetadataReader stuMetadataReader = metadataReaderFactory.getMetadataReader("com.zzhua.test.Student");
        boolean annotated = stuMetadataReader.getAnnotationMetadata().isAnnotated(Component.class.getName());
        System.out.println(annotated);

		Map<String, Object> annotationAttributes = stuMetadataReader.getAnnotationMetadata().getAnnotationAttributes(Component.class.getName());
        System.out.println(annotationAttributes); // {value=s1}

        MultiValueMap<String, Object> allAnnotationAttributes = stuMetadataReader.getAnnotationMetadata().getAllAnnotationAttributes(Component.class.getName());
        System.out.println(allAnnotationAttributes); // {value=[s1, ]}
    }
	// 结果只输出了 true, 静态代码块并未执行, 说明这种方式下, 
	// spring是没有加载这个类的, 但是可以获取到类上面是否包含指定的注解
}

2.4.2 @Conditional衍生注解

由@Conditional注解衍生而来的注解,这些注解上都标注了@Conditinal注解,并且指定了Condition接口的实现类,当满足指定的条件时,被该注解标注的类才会生效。

@ConditionalOnBean               当容器中由指定bean时
@ConditionalOnMissingBean        当在容器中没有指定bean时
@ConditionalOnClass              当在类路径下有指定类时
@ConditionalOnMissingClass       当在类路径下没有有指定类时
@ConditionalOnProperty           当指定的属性有指定的值时
@ConditionalOnExpression         当spel表达式为true时
@ConditionalOnWebApplication     当在web容器时
...

以@ConditionalOnMissingBean 为例

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class) // 注意这里有个@Conditional注解, 并且有OnBeanCondition条件要满足
public @interface ConditionalOnMissingBean {

	Class<?>[] value() default {};

	String[] type() default {};

	Class<?>[] ignored() default {};

	String[] ignoredType() default {};
	
	Class<? extends Annotation>[] annotation() default {};

	String[] name() default {};

	SearchStrategy search() default SearchStrategy.ALL;

	Class<?>[] parameterizedContainer() default {};
}

OnBeanCondition

@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
	
	@Override
	public ConfigurationPhase getConfigurationPhase() {
		return ConfigurationPhase.REGISTER_BEAN;
	}

	// 此方法用于重写FilteringSpringBootCondition抽象类中的方法
	// 		根据spring-autoconfigure-metadata.properties文件对自动配置类再次筛选
	@Override
	protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			                                       AutoConfigurationMetadata autoConfigurationMetadata) {
		// 
	}

	// 该类同时处理: @ConditionalOnBean、
	//             @ConditionalOnSingleCandidate、
	//             @ConditionalOnMissingBean
	//            这3个注解
	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
	
		ConditionMessage matchMessage = ConditionMessage.empty();
		
		MergedAnnotations annotations = metadata.getAnnotations();
		
		if (annotations.isPresent(ConditionalOnBean.class)) {
			// ... 对@ConditionalOnBean注解的处理
		}
		
		if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
			// ... 对@ConditionalOnSingleCandidate注解的处理
		}

		// 如果 方法或类 上有@ConditionalOnMissingBean注解
		if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
			// 封装到Spec中
			Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
					ConditionalOnMissingBean.class);
			
			// 从容器中根据@ConditionalOnMissingBean注解中的条件匹配
			MatchResult matchResult = getMatchingBeans(context, spec);
			
			// 如果根据条件找到了满足条件的bean, 那就是不匹配
			if (matchResult.isAnyMatched()) {
				String reason = createOnMissingBeanNoMatchReason(matchResult);
				// 返回条件不满足
				return ConditionOutcome.noMatch(spec.message().because(reason));
			}

			// 没有找到满足条件的bean信息
			matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
		}
		// 匹配
		return ConditionOutcome.match(matchMessage);
	}

	// 省略...
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 10:59:34  更:2022-09-13 11:00:05 
 
开发: 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/23 12:11:26-

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