入职第三周,对Spring Cloud的核心之一Spring Boot进行了学习,在这里对Spring Boot的自动装配的产生背景、实现过程和原理进行探讨、分析。
Spring Boot - IoC/DI
Spring Boot是构建Spring Cloud生态的基石,简单来说它是帮助开发者快速构建一个基于Spring Framework以及Spring生态体系的应用解决方案,也是Spring Framework对于“约定优于配置“理念的最佳实践。
Spring是一个轻量级框架,它的主要目的是简化JavaEE的企业级开发应用,而达到这个目的两个关键手段是IoC/DI和AOP,其中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容器中
举个例子:
-
添加Starter依赖
-
在application.properties中配置Redis的数据源
-
在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自动装配的产生背景、实现过程和原理的理解分享。
不足和错误请大家指出,谢谢!