JAVA自学指路-Spring Boot自动装配的原理

787 阅读7分钟

 入职第三周,对Spring Cloud的核心之一Spring Boot进行了学习,在这里对Spring Boot的自动装配产生背景实现过程原理进行探讨、分析。

 Spring Boot - IoC/DI

Spring Boot是构建Spring Cloud生态的基石,简单来说它是帮助开发者快速构建一个基于Spring Framework以及Spring生态体系的应用解决方案,也是Spring Framework对于“约定优于配置“理念的最佳实践。

Spring是一个轻量级框架,它的主要目的是简化JavaEE的企业级开发应用,而达到这个目的两个关键手段是IoC/DIAOP,其中Spring中的IoC/DI和Spring Boot的核心有比较大的关系,所以在此着重探讨一下IoC/DI。

Spring IoC/DI

IoC和DI全称分别是控制反转(Inversion of Control)依赖注入(Dependency Injection)

IoC实际上就是把对象的生命周期托管到了Spring容器中,之后客户端不再需要在通过new来创建这些对象,而是直接从IoC容器其中获得。在早期的Spring中,主要通过XML的方式来定义Bean,Spring会解析XML文件,把定义的Bean装载到IoC容器中,如图:

DI简单来说就是在Spring容器运行期间,动态地把对象之间的依赖关系注入到组件中,如某个对象的其中一个属性为另外一个对象。实现依赖注入的方法有三种,分别是接口注入、构造方法和setter方法。不过现在基本上都基于注解的方式来描述Bean之间的依赖关系,比如**@Autowired、@Inject和@Resource**。但不管形式怎样,本质还是一样的。

Bean装配方式的升级

Spring从2.x开始,可以使用注解的方式来对Bean进行声明和注入,大大减少了XML的配置量。升级到3.x后,提供了JavaConfig的能力,可以达到完全取代XML的效果。所以,我们现在所使用的Spring Framework或者Spring Boot,已经看不到XML配置的存在了。

具体配置方式不在此举例了。

虽然通过注解的方式来装配Bean,可以很大承担上减少XML配置带来的问题,但是换汤不换药,本质问题没有解决,比如①依赖过多②配置过多③运行和部署很麻烦等等。

还好,Spring Boot诞生了。

Spring Boot

Spring Boot并不是一个新的技术框架,其主要作用就是简化Spring应用的开发,开发者只需要通过少量的代码就可以创建一个产品级的Spring应用,而达到这一目的的最核心思想就是”约定优于配置“。

"约定优于配置"是一种 软件设计模式,目的在于减少配置的数量或者降低难度,从而提升开发效。

在Spring Boot中,约定优于配置的思想主要体现下以下几个方面:

  • Maven目录结构的约定
  • Spring Boot默认的配置文件及配置文件中的配置属性的约定
  • 对于Spring MVC的依赖,自动依赖内置的Tomcat容器
  • 对于Starter组件自动完成装配

Sring Boot是基于Spring Framwork体系来构建的,它没有什么新鲜的东西,但是核心还是要掌握:

  • Starter组件,提供开箱即用的组件
  • 自动装配,自动根据上下文完成Bean的装配
  • Actuator,Spring Boot的应用监控
  • Spring Boot CLI,基于命令行工具快速构建Spring Boot应用

最核心的是自动装配,Starter组件的核心部分也是基于自动装配来实现的

Spring Boot自动装配

在Spring Boot中,自动装配是Starter的基础,也是Spring Boot的核心

简单来说,自动装配就是自动将Bean装配到Spring容器中

举个例子:

  1. 添加Starter依赖

  2. 在application.properties中配置Redis的数据源

  3. 在CategoryController中实现Mybatis操作

在上面的例子中没有通过XML形式或者注解的形式把Mybatis注入Spring容器中,但是在CategoryController中却可以直接通过CategoryMapper进行Mybatis的操作,这就说明Spring容器中已经存在Mybatis

现在我们来探究以下,如何只添加一个Starter依赖,就能完成该依赖组件相关Bean的自动注入?

Spring Boot自动装配的实现过程

自动装配在Spring Boot中是通过**@EnableAutoConfiguration**注解来开启的,这个注解的声明在启动类注解@SpringBootApplication内。

使用@Enable注解后,Spring会解析到**@Import**导入的配置类,从而根据这个配置类中的描述来实现Bean的装配

@SpringBootApplication和普通的@Enable又有稍有不同。

  • 多了一个**@AutoConfigurationPackage**注解,它可以把该注解类所在的包以及子包下所有的组件扫描到Spring容器中
  • Import注解中导入的并不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了ImportSelector,它只有一个selectImports抽象方法,返回一个String数组,在这个数组中可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类后,会把该实现类中的返回的Class名词都装在Spring容器中,整个过程如下:

@Import优点:①批量装配②通过逻辑处理来实现bean的选择性装配

Spring Boot自动装配的原理解析

自动装配的核心是扫描约定目录下的文件进行解析,解析完成之后,把得到的Configuration配置类通过ImportSelector导入,从而完成Bean的自动装配过程

定位到AutoConfigurationImportSelector.class中的selectImports方法,是ImportSelector接口方法的实现,主要有两个功能:①自动加载自动装配的条件元数据②收集所有符合条件的配置类

重点分析下上段代码中间的getAutoConfigurationEntry方法,作用是获得所有需要自动装配的配置类。总的来说,首先获得所有的配置类,再通过去重、exclude排除等操作,最终得到需要实现自动装配的配置类

getCandidateConfigurations是获得配置类最核心的方法

这里用到了SpringFactoriesLoader,它是Spring内部提供的一种约定俗成的加载方式。简单来说,它会扫描classpath下的META-INF/spring.factories文件,其中的数据以键值对存储,而SpringFactoriesLoader.loadFactoryNames会根据Key得到对应的value值。本场景中,Key对应的为EnableAutoConfiguration,value是多个配置类

自动装配的原理基本上分析完了,简单来总结一下核心过程

  • 通过**@Import(AutoConfigurationImportSelector)**实现配置类的导入,但是这里并不是传统意义上的单个配置类装配
  • AutoConfigurationImportSelector实现了ImportSelecotr接口,重写了方法selectImports**,它用于实现选择批量配置类的装配**
  • 通过SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories,读取需要实现自动装配的配置类
  • 通过筛选条件,把不符合条件的配置类移除,最终完成自动装配

Spring Boot条件装配

@Conditional是Spring Framework提供的一个核心注解,这个注解的作用是提供自动装配的条件约束,一般配合@Configuration和@Bean使用。

简单来说,Spring在解析@Configuration配置类时,如果该配置增加了@Conditional注解,那么会根据注解配置来决定是否要实现Bean的配置。

@Conditional()的参数接受一个实现了Condition接口的函数的数组,在这个接口的mathes方法会返回boolean,true则装载,反之则不。

在Spring Boot中,针对@Conditional做了扩展,提供了更简单的使用形式

  • ConditionalOnBean/ ConditionalOnMissingBean存在或不存在某个Bean进行装载

  • ConditionalOnClass/ ConditionalOnMissingClass:classpath存在或不存在某个类进行装载

  • ConditionalOnCloudPlatform只在指定云平台上加载Bean

  • ConditionalOnJava指定版本java

  • ConditionalOnJndi指定资源通过JNDI才加载

  • 等等

Spring Boot中还提供了spring-autoconfigure-metadata.properties文件来实现批量自动装配条件配置。作用和@Conditional是一样的,只是将这些条件配置放在了配置文件中。

以上就是我对Spring Boot自动装配的产生背景、实现过程和原理的理解分享。

不足和错误请大家指出,谢谢!