SpringBoot自动装配原理解析

2021-05-28 15:02

阅读:653

标签:utc   from   prefix   enabled   elements   def   run   封装   ESS   

自动装配是 Spring Boot 的核心部分,也是 Spring Boot 功能的基础,正是由于自动装配,才将我们从 Bean 的繁复配置中解脱出来。那么 Spring Boot 中的自动装配指的是什么?我们继 续以 Spring MVC 为例,不使用 Spring Boot 时,我们可能需要配置视图解析器,文件解析器, 请求适配器等等各种 Bean , 如果在使用数据库 Redis ,还需要配置数据库Redis相关Bean。

一、从@SpringBootApplication 启动注解入手

源码如下:

@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 {	@AliasFor(annotation = EnableAutoConfiguration.class) 	Class>[] exclude() default {}; 	@AliasFor(annotation = EnableAutoConfiguration.class) 	String[] excludeName() default {};//根据包路径扫描 	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages") 	String[] scanBasePackages() default {};//直接根据 class 类扫描 	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") 	Class>[] scanBasePackageClasses() default {}; }

初看 @SpringBootApplication 有很多的注解组成,其实归纳就是一个 三体 结构,重要的只有三个 Annotation :

  1. @Configuration( @SpringBootConfiguration 实质就是一个 @Configuration )
  2. @EnableAutoConfiguration
  3. @ComponentScan

也就是说我们在开发的时候,加上上面的三个注解会等同于加上 @SpringBootApplication 注解。

@Configuration 注解 :

这个注解实际上就是代表了一个配置类,相当于一个 beans.xml 文件 ;

@ComponentScan 注解:

@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些bean 定义加载到容器中 ;

@EnableAutoConfiguration 注解:

在 spring 中有关于 @Enablexxx 的注解是开启某一项功能的注解,比如 @EnableScheduling表示开启 spring 的定时任务。其原理是借助 @Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 Ioc 容器;

EnableAutoConfiguration 代表开启 springboot 的自动装配。

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { 	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; //按类型排序不需要自动装配的类 	Class>[] exclude() default {}; //按名称排除不需要自动装配的类 	String[] excludeName() default {}; }

从上面源码中我们可以知道,最关键的要属 @Import(EnableAutoConfigurationImportSelector.class) ,借助 EnableAutoConfigurationImportSelector 和 @EnableAutoConfiguration 可以帮助 Spring Boot 应用将所有符合条件的 @Configuration 配置都加载到当前SpringBoot 创建并使用的 IoC 容器。同时借助于 Spring 框架原有的一个工具类: SpringFactoriesLoader , @EnableAutoConfiguration 就可以实现智能的自动配置。

从这里可以看出该类实现很多的 xxxAware 和 DeferredImportSelector ,所有的 aware 都优先于 selectImports ;

方法执行,也就是说 selectImports 方法最后执行,那么在它执行的时候所有需要的资源都已经获取到了

public class AutoConfigurationImportSelector implements  DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... public String[] selectImports(AnnotationMetadata annotationMetadata) { 	if (!this.isEnabled(annotationMetadata)) { 	return NO_IMPORTS; } else { //1. 加载 META-INF/spring-autoconfigure-metadata.properties文件 	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//2. 获取注解的属性及其值(PS:注解指的是@EnableAutoConfiguration 注解) 	AnnotationAttributes attributes = this.getAttributes(annotationMetadata);//3. 在classpath下所有的 META-INF/spring.factories 文件中查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值, 并将其封装到一个 List 中返回 	List configurations =this.getCandidateConfigurations(annotationMetadata, attributes); //4. 对上一步返回的 List 中的元素去重、排序 	configurations = this.removeDuplicates(configurations);//5.依据第 2 步中获取的属性值排除一些特定的类 	Set exclusions = this.getExclusions(annotationMetadata, attributes);//6. 对上一步中所得到的 List 进行过滤,过滤的依据是条件匹配。这里用到的过滤器是org.springframework.boot.autoconfigure.condition.OnClassCondition最终返回的是一个 ConditionOutcome[]数组。//(PS:很多类都是依赖于其它的类的,当有某个类时才会装配,所以这次过滤的就是根据是否有某个class进而决定是否装配的。这些类所依赖的类都写在META-INF/spring-autoconfigure-metadata.properties 文件里)	this.checkExcludedClasses(configurations, exclusions); 	configurations.removeAll(exclusions); 	configurations = this.filter(configurations,autoConfigurationMetadata);	this.fireAutoConfigurationImportEvents(configurations,exclusions); 	return StringUtils.toStringArray(configurations); 	} }protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 	List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderF actoryClass(), this.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 中加载配置, SpringFactoriesLoader 属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置, 即根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key, 获取对应的一组 @Configuration 类。

public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static MapString, ListString>> loadSpringFactories(@Nullable ClassLoader classLoader) { 	MultiValueMapString, String> result = cache.get(classLoader); 	if (result != null) 	return result; 	try { 		Enumeration urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 	result = new LinkedMultiValueMap(); 	while (urls.hasMoreElements()) { 		URL url = urls.nextElement(); 		UrlResource resource = new UrlResource(url);Properties properties = 		PropertiesLoaderUtils.loadProperties(resource); 		for (Map.Entry, ?> entry : properties.entrySet()) { 			ListString> factoryClassNames = Arrays.asList( 				StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); 			result.addAll((String) entry.getKey(), factoryClassNames); 		} 	} 		cache.put(classLoader, result); 		return result; }catch (IOException ex) { 	throw new IllegalArgumentException("Unable to load  	factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); 	} }...

总结:

@EnableAutoConfiguration 作用就是从 classpath 中搜寻所有的 META-INF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了 @Configuration 的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到 IoC 容器。

这些功能配置类要生效的话,会去 classpath 中找是否有该类的依赖类(也就是pom.xml 必须有对应功能的 jar 包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

所以功能类能生效需要的 条件:

  1. spring.factories 里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类);
  2. pom.xml里面需要有对应的JAR包。

二、自动装配案例说明 Redis为例

1、从 spring-boot-autoconfigure.jar/META-INF/spring.factories 中获取Redis的相关配置类全限定名(有 120 多个的配置类) RedisAutoConfiguration , 一般一个功能配置类围绕该功能,负责管理创建多个相关的功能类,比如 RedisAutoConfiguration 负责: JedisConnectionFactory 、 RedisTemplate 、 StringRedisTemplate 这3个功能类的创建 spring.factories 中的Redis配置类 。

2、 RedisAutoConfiguration 配置类生效的一个条件是在 classpath 路径下有 RedisOperations 类存在,因此 Spring Boot 的自动装配机制会去 classpath 下去查找对应的 class 文件。

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { ...}

3、如果 pom.xml 有对应的 jar 包,就能匹配到对应依赖 class

dependency> 	groupId>org.springframework.bootgroupId> 	artifactId>spring-boot-starter-data-redisartifactId> dependency>

4、匹配成功, 这个功能配置类才会生效, 同时会注入默认的属性配置类 @EnableConfigurationProperties(RedisProperties.class)

@ConfigurationProperties(prefix = "spring.redis") 	public class RedisProperties { 	private int database = 0; 	private String url; 	private String host = "localhost"; 	private String password; 	private int port = 6379; ...

5、Redis功能配置里面会根据条件生成最终的 JedisConnectionFactory 、 RedisTemplate ,并提供了默认的配置形式`@ConditionalOnMissingBean(name =

"redisTemplate") `

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration {	@Bean //用户没定义就使用默认的 	@ConditionalOnMissingBean(name = "redisTemplate") 	public RedisTemplate redisTemplate( 		RedisConnectionFactory redisConnectionFactory) throws  UnknownHostException { 	  RedisTemplate template = new RedisTemplate(); 	  template.setConnectionFactory(redisConnectionFactory); 	  return template;     }    @Bean 	@ConditionalOnMissingBean(StringRedisTemplate.class) 	public StringRedisTemplate stringRedisTemplate( 		RedisConnectionFactory redisConnectionFactory) throws  UnknownHostException {       StringRedisTemplate template = new StringRedisTemplate(); 	  template.setConnectionFactory(redisConnectionFactory);       return template; 	} }

6、最终创建好的默认装配类,会通过功能配置类里面的 @Bean 注解,注入到 IOC 当中

7、用户使用,当用户在配置文件中自定义时候就会覆盖默认的配置 @ConditionalOnMissingBean(name = "redisTemplate")

三、自动依赖过程总结

1、通过各种注解实现了类与类之间的依赖关系,容器在启动的时候 Application.run ,会调用 EnableAutoConfigurationImportSelector.class 的 selectImports 方法(其实是其父类的方法)

这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨。

2、 selectImports 方法最终会调用 SpringFactoriesLoader.loadFactoryNames 方法来获取一个全面的常用 BeanConfiguration 列表。

3、 loadFactoryNames 方法会读取 FACTORIES_RESOURCE_LOCATION (也就是 spring-boot-autoconfigure.jar 下面的 spring.factories),获取到所有的Spring 相关的 Bean 的全限定名 ClassName,大概 120 多个 。

4、 selectImports 方法继续调用 filter(configurations, autoConfigurationMetadata);这个时候会根据这些 BeanConfiguration 里面的条件,来一一筛选,最关键的是 @ConditionalOnClass ,这个条件注解会去 classpath 下查找,jar 包里面是否有这个条件依赖类,所以必须有了相应的 jar 包,才有这些依赖类,才会生成 IOC 环境需要的一些默认配置 Bean 。

5、最后把符合条件的 BeanConfiguration 注入默认的 EnableConfigurationPropertie 类里面的属性值,并且注入到 IOC 环境当中。

SpringBoot自动装配原理解析

标签:utc   from   prefix   enabled   elements   def   run   封装   ESS   

原文地址:https://www.cnblogs.com/cqqfboy/p/14781461.html


评论


亲,登录后才可以留言!