Spring源码学习二

@EnableAutoConfiguration注解的实现原理
了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就
容易一些了
它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector

@Import(AutoConfigurationImportSelector.class)

从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过
Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都
会被纳入到spring容器中。
那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的
selectImports方法
selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	// 从配置文件(spring-autoconfigure-metadata.properties)中加载
	AutoConfigurationMetadata
	AutoConfigurationMetadata autoConfigurationMetadata =
	AutoConfigurationMetadataLoader
	.loadMetadata(this.beanClassLoader);
	// 获取所有候选配置类EnableAutoConfiguration
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
	autoConfigurationMetadata, annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(
	AutoConfigurationMetadata autoConfigurationMetadata,
	AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	//获取元注解中的属性
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//使用SpringFactoriesLoader 加载classpath路径下META-INF\spring.factories中,
	//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value
	List<String> configurations = getCandidateConfigurations(annotationMetadata,
	attributes);
	//去重
	configurations = removeDuplicates(configurations);
	//应用exclusion属性
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不
	被加载
	configurations = filter(configurations, autoConfigurationMetadata);
	//广播事件
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都
加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类
SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要
加载的bean进行条件过滤

SpringFactoriesLoader
分析一下SpringFactoriesLoader这个工具类的使用。它其实和
java中的SPI机制的原理是一样的,不过它比SPI更好的点在于不会一次性加载所有的类,而是根据key进
行加载。
首先,SpringFactoriesLoader的作用是从classpath/META-INF/spring.factories文件中,根据key来加
载对应的类到spring IoC容器中。接下来带大家实践一下
创建外部项目jar

<dependency>
	<groupId>org.springframeworkgroupId>
	<artifactId>spring-contextartifactId>
	<version>4.3.13.RELEASEversion>
dependency>

创建bean以及config

public class mashibingCore {
	public String study(){
		System.out.println("good good study, day day up");
		return "mashibingEdu.com";
	}
}
@Configuration
public class mashibingConfig {
@Bean
public mashibingCore mashibingCore(){
	return new mashibingCore();
	}
}

创建另外一个工程(spring-boot)
把前面的工程打包成jar,当前项目依赖该jar包

<dependency>
	<groupId>com.mashibingedu.practicegroupId>
	<artifactId>mashibing-CoreartifactId>
	<version>1.0-SNAPSHOTversion>
dependency>

通过下面代码获取依赖包中的属性
运行结果会报错,原因是mashibingCore并没有被Spring的IoC容器所加载,也就是没有被
EnableAutoConfiguration导入

@SpringBootApplication
public class SpringBootStudyApplication {
	public static void main(String[] args) throws IOException {
		ConfigurableApplicationContext
		ac=SpringApplication.run(SpringBootStudyApplication.class, args);
		mashibingCore Myc=ac.getBean(mashibingCore.class);
		System.out.println(Myc.study());
	}
}

解决方案
在mashibing-Core项目resources下新建文件夹META-INF,在文件夹下面新建spring.factories文件,
文件中配置,key为自定配置类EnableAutoConfiguration的全路径,value是配置类的全路径

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mashibingedu.
practice.mashibingConfig

重新打包,重新运行SpringBootStudyApplication这个类。
可以发现,我们编写的那个类,就被加载进来了。

Spring Boot中的条件过滤
在分析AutoConfigurationImportSelector的源码时,会先扫描spring-autoconfigurationmetadata.properties文件,最后在扫描spring.factories对应的类时,会结合前面的元数据进行过滤,
为什么要过滤呢? 原因是很多的@Configuration其实是依托于其他的框架来加载的,如果当前的
classpath环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以有
效的减少@configuration类的数量从而降低SpringBoot的启动时间。
修改mashibing-Core
在META-INF/增加配置文件,spring-autoconfigure-metadata.properties。

com.mashibingedu.practice.mashibingConfig.ConditionalOnClass=com.mashibingedu.Te
stClass

格式:自动配置的类全名.条件=值
上面这段代码的意思就是,如果当前的classpath下存在TestClass,则会对mashibingConfig这个
Configuration进行加载
演示过程(spring-boot)

  1. 沿用前面spring-boot工程的测试案例,直接运行main方法,发现原本能够被加载的
    mashibingCore,发现在ioc容器中找不到了。
public static void main(String[] args) throws IOException {
	ConfigurableApplicationContext
	ac=SpringApplication.run(SpringBootStudyApplication.class, args);
	mashibingCore Myc=ac.getBean(mashibingCore.class);
	System.out.println(Myc.study());
}

  1. 在当前工程中指定的包com.mashibingedu下创建一个TestClass以后,再运行上面这段代码,程序
    能够正常执行

你可能感兴趣的:(学习,spring,学习,java)