原理解析_SpringBoot自动装配原理解析
自动装配是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
:
@Configuration(
@SpringBootConfiguration
实质就是一个@Configuration
)@EnableAutoConfiguration
@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 Map> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap 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()) { List 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 包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。
所以功能类能生效需要的条件:
- spring.factories 里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类);
- 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
org.springframework.boot spring-boot-starter-data-redis
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自动装配原理解析相关推荐
- Spring Boot自动装配过程解析及简单Demo演示
文章目录 1.约定大于配置 2.自动装配原理 2.1.`@SpringBootApplication` 2.2.`@EnableAutoConfiguration` 2.3.`@Import` 2.4 ...
- spring自动装配原理
为了搞明白自动装配原理,需要知道spring容器管理bean的生命周期 Spring Bean 生命周期流程图 bean自身方法的生命周期 分为四步: //执行此段代码,spring容器的bean执行 ...
- Spring Boot自动装配原理详解
目录 1.环境和依赖 1.1.spring boot版本 1.2.依赖管理 2.自动装配 2.1.流程概述 2.2.三大步前的准备工作 2.2.1.注解入口 2.2.2.获取所有配置类 2.3.获取过 ...
- SpringBoot-起步依赖与自动装配原理
简化开发,约定大于配置,开箱即用,提供了各种默认配置来简化项目 1. 起步依赖分析 在起步依赖上,SpringBoot 帮我们管理了各个依赖的版本,使各个依赖不会出现版本冲突:另外,Spring Bo ...
- Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理
目录 一.背景 1.1.主类的加载 1.2.后置处理器的获取 二.配置类后处理器 2.1.获取配置类 2.2. 2.3.解析主类 2.3.1.整体解析过程 2.3.2.核心解析过程 2.3.3.延迟导 ...
- 【SpringBoot】自动装配原理
[SpringBoot]自动装配原理 文章目录 [SpringBoot]自动装配原理 一.pom.xml 1.spring-boot-dependencies 2.spring-boot-starte ...
- springboot 整合redis_springboot自动装配原理详解
1)传统ssm整合redis的时候 需要在xml的配置文件中 进行大量的配置Bean 我们在这里使用springboot来代替ssm的整合,只是通过xml的形式来整合redis 第一步:加入配置 &l ...
- springboot 项目起步讲解及自动装配原理
哈喽~大家好,这篇看看springboot 项目起步讲解及自动装配原理.
- 【理解springboot自动装配原理】
理解springboot自动装配原理: 最近读了小马哥(mercyblitz)Springboot编程思想(核心篇),有了一些心得和感悟,分享给大家: 1. 官网介绍了激活自动装配的方法: * 文档提 ...
最新文章
- unity3d做简单小游戏可以吗?
- itstime后面跟什么_一文讲透什么是引流
- 知识管理中的矛盾分析
- QQ 一键加群、扫二维码加群 - 腾讯官方API文档接入
- python用符号计算检验多维数组的计算
- java程序a-z b-y,请完成下列Java程序:对大写的26个英文字母加密,从键盘输入一个大写字母串,输出这个串加密后的结 - 赏学吧...
- JAVA定时器ScheduledExecutorService中,scheduleAtFixedRate和scheduleWithFixedDelay的区别
- Item08. 多级指针(Pointers to Pointers)
- [剑指Offer] 43.左旋转字符串
- Python__关于列表的引用 以append操作为例
- 大样本OLS模型假设及R实现
- 关于javascript和typescript学习总结
- 3dmax联机分布式渲染方法技巧详解
- 企业微信客户端web页面调试
- 华为核心交换如何配置源地址转换_华为路由器和交换机配置地址转换
- java对象复制_Java对象的复制三种方式
- 删除后别人的微信号变成wxid_腾讯开放微信号修改,一年一次,方法简单
- 怎么安装java_怎么安装打印机到电脑步骤
- 共享虚拟主机是不是服务器,共享虚拟主机、独享虚拟主机还是云服务器?
- python 六大数据类型
热门文章
- python任务栏通知区域_python+pyqt实现右下角弹出框
- [codevs 1033] 蚯蚓的游戏问题
- ICCV 2017 《Unsupervised Learning from Video to Detect Foreground Objects in Single Images》论文笔记
- C++的黑科技之进制转换
- Luogu P5564 [Celeste-B]Say Goodbye (多项式、FFT、Burnside引理、组合计数)
- php输出一百个hello,如何使用 PHP 输出 hello world?
- c语言recv函数返回值,谈谈recv()函数的返回值
- css 高度塌陷_HTML+CSS入门 HTML高度塌陷以及定位详解
- e站host地址_Linux系统怎么使用命令行查询公网IP地址
- python简说(二十二)写日志