自动装配是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 就可以实现智能的自动配置。

从这里可以看出该类实现很多的xxxAwareDeferredImportSelector,所有的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 包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

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

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

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

1、从spring-boot-autoconfigure.jar/META-INF/spring.factories中获取Redis的相关配置类全限定名(有 120 多个的配置类)RedisAutoConfiguration, 一般一个功能配置类围绕该功能,负责管理创建多个相关的功能类,比如RedisAutoConfiguration负责:JedisConnectionFactoryRedisTemplateStringRedisTemplate 这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功能配置里面会根据条件生成最终的JedisConnectionFactoryRedisTemplate,并提供了默认的配置形式`@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.classselectImports 方法(其实是其父类的方法)

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

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自动装配原理解析相关推荐

  1. Spring Boot自动装配过程解析及简单Demo演示

    文章目录 1.约定大于配置 2.自动装配原理 2.1.`@SpringBootApplication` 2.2.`@EnableAutoConfiguration` 2.3.`@Import` 2.4 ...

  2. spring自动装配原理

    为了搞明白自动装配原理,需要知道spring容器管理bean的生命周期 Spring Bean 生命周期流程图 bean自身方法的生命周期 分为四步: //执行此段代码,spring容器的bean执行 ...

  3. Spring Boot自动装配原理详解

    目录 1.环境和依赖 1.1.spring boot版本 1.2.依赖管理 2.自动装配 2.1.流程概述 2.2.三大步前的准备工作 2.2.1.注解入口 2.2.2.获取所有配置类 2.3.获取过 ...

  4. SpringBoot-起步依赖与自动装配原理

    简化开发,约定大于配置,开箱即用,提供了各种默认配置来简化项目 1. 起步依赖分析 在起步依赖上,SpringBoot 帮我们管理了各个依赖的版本,使各个依赖不会出现版本冲突:另外,Spring Bo ...

  5. Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理

    目录 一.背景 1.1.主类的加载 1.2.后置处理器的获取 二.配置类后处理器 2.1.获取配置类 2.2. 2.3.解析主类 2.3.1.整体解析过程 2.3.2.核心解析过程 2.3.3.延迟导 ...

  6. 【SpringBoot】自动装配原理

    [SpringBoot]自动装配原理 文章目录 [SpringBoot]自动装配原理 一.pom.xml 1.spring-boot-dependencies 2.spring-boot-starte ...

  7. springboot 整合redis_springboot自动装配原理详解

    1)传统ssm整合redis的时候 需要在xml的配置文件中 进行大量的配置Bean 我们在这里使用springboot来代替ssm的整合,只是通过xml的形式来整合redis 第一步:加入配置 &l ...

  8. springboot 项目起步讲解及自动装配原理

    哈喽~大家好,这篇看看springboot 项目起步讲解及自动装配原理.

  9. 【理解springboot自动装配原理】

    理解springboot自动装配原理: 最近读了小马哥(mercyblitz)Springboot编程思想(核心篇),有了一些心得和感悟,分享给大家: 1. 官网介绍了激活自动装配的方法: * 文档提 ...

最新文章

  1. unity3d做简单小游戏可以吗?
  2. itstime后面跟什么_一文讲透什么是引流
  3. 知识管理中的矛盾分析
  4. QQ 一键加群、扫二维码加群 - 腾讯官方API文档接入
  5. python用符号计算检验多维数组的计算
  6. java程序a-z b-y,请完成下列Java程序:对大写的26个英文字母加密,从键盘输入一个大写字母串,输出这个串加密后的结 - 赏学吧...
  7. JAVA定时器ScheduledExecutorService中,scheduleAtFixedRate和scheduleWithFixedDelay的区别
  8. Item08. 多级指针(Pointers to Pointers)
  9. [剑指Offer] 43.左旋转字符串
  10. Python__关于列表的引用 以append操作为例
  11. 大样本OLS模型假设及R实现
  12. 关于javascript和typescript学习总结
  13. 3dmax联机分布式渲染方法技巧详解
  14. 企业微信客户端web页面调试
  15. 华为核心交换如何配置源地址转换_华为路由器和交换机配置地址转换
  16. java对象复制_Java对象的复制三种方式
  17. 删除后别人的微信号变成wxid_腾讯开放微信号修改,一年一次,方法简单
  18. 怎么安装java_怎么安装打印机到电脑步骤
  19. 共享虚拟主机是不是服务器,共享虚拟主机、独享虚拟主机还是云服务器?
  20. python 六大数据类型

热门文章

  1. python任务栏通知区域_python+pyqt实现右下角弹出框
  2. [codevs 1033] 蚯蚓的游戏问题
  3. ICCV 2017 《Unsupervised Learning from Video to Detect Foreground Objects in Single Images》论文笔记
  4. C++的黑科技之进制转换
  5. Luogu P5564 [Celeste-B]Say Goodbye (多项式、FFT、Burnside引理、组合计数)
  6. php输出一百个hello,如何使用 PHP 输出 hello world?
  7. c语言recv函数返回值,谈谈recv()函数的返回值
  8. css 高度塌陷_HTML+CSS入门 HTML高度塌陷以及定位详解
  9. e站host地址_Linux系统怎么使用命令行查询公网IP地址
  10. python简说(二十二)写日志