SpringBoot 自动装配原理
SpringBoot
SpringBoot 自动装配原理
为什么要使用 SpringBoot 自动装配?
- 使用 Spring 框架的 XML 配置文件过于繁琐,即使 Spring 后面引入了基于注解的配置,在开启某些 Spring 特性或者引入第三方依赖的时候,还是需要 XML 或 Java 进行显示配置。
- 使用 SpringBoot 以后,只需添加相关依赖,无需配置,这得益于自动装配,自动装配可以说是 SpringBoot 的核心。
什么是 SpringBoot 自动装配?
自动装配:通过注解或者一些简单的配置就能在 SpringBoot 的帮助下实现某块功能。
在提到自动装配的时候,一般会和 SpringBoot 联系在一起。但实际上 Spring Framework 早就实现了这个功能。SpringBoot 只是在其基础上,通过 SPI 的方式,做了进一步优化。
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器中(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
没有 SpringBoot 的情况下,就需要引入第三方依赖,需要手动配置,非常麻烦。但在 SpringBoot 中,直接引入一个 starter 即可,之后通过一些少量注解和一些简单的配置就能使用第三方组件提供的功能了。
比如要在项目中引入 redis 的话,在项目中引入对应的 starter 即可。
SpringBoot 如何实现自动装配?
先看看 SpringBoot 的核心注解 @SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
大概可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制@Configuration
:允许在上下文中注册额外的 bean 或导入其他配置类@ComponentScan
:扫描被@Component
(@Service
,@Controller
) 注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter
和AutoConfigurationExcludeFilter
。
@EnableAutoConfiguration
是实现自动装配的核心注解。
而 @EnableAutoConfiguration
只是一个简单的注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector
类。
AutoConfigurationImportSelector
:加载自动装配类
AutoConfigurationImportSelector
类的继承体系如下:
**AutoConfigurationImportSelector
类实现了ImportSelector
接口,也就实现了这个接口中的selectImports
**方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 Ioc 容器中。
重点关注一下 getAutoConfigurationEntry()
方法,这个方法主要负责加载自动配置类的。
该方法调用链如下:
结合 getAutoConfigurationEntry()
的源码来详细分析一下:
其中 private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
第 1 步:判断自动装配开关是否打开。默认 spring.boot.enableautoconfiguration=true
,可在 application.properties
或 application.yml
中设置。
第 2 步:用于获取 EnableAutoConfiguration
注解中的 exclude
和 excludeName
。
第 3 步:获取需要自动装配的所有配置类,读取 META-INF/spring.factories
从下图可以看到这个文件的配置内容都被我们读取到了。XXXAutoConfiguration
的作用就是按需加载组件。
不光是这个依赖下的 META-INF/spring.factories
被读取到,所有 Spring Boot Starter 下的 META-INF/spring.factories
都会被读取到。
所以,你可以清楚滴看到, druid 数据库连接池的 Spring Boot Starter 就创建了 META-INF/spring.factories
文件。
如果,我们自己要创建一个 Spring Boot Starter,这一步是必不可少的。
第 4 步:spring.factories
中这么多配置,每次启动都要全部加载么?
很明显,这是不现实的。我们 debug 到后面你会发现,configurations
的值变小了。
因为,这一步有经历了一遍筛选,@ConditionalOnXXX
中的所有条件都满足,该类才会生效。
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel 是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
Spring Boot 提供的条件注解
@ConditionalOnBean
:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass
:当类路径下有指定类的条件下@ConditionalOnMissingClass
:当类路径下没有指定类的条件下@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnResource
:类路径是否有指定的值@ConditionalOnExpression
:基于 SpEL 表达式作为判断条件@ConditionalOnJava
:基于 Java 版本作为判断条件@ConditionalOnJndi
:在 JNDI 存在的条件下差在指定的位置@ConditionalOnNotWebApplication
:当前项目不是 Web 项目的条件下@ConditionalOnWebApplication
:当前项目是 Web 项 目的条件下
如何实现 SpringBoot 自动装配?
第一步,创建 threadpool-spring-boot-starter
工程,引入 Spring Boot 相关依赖;
第二步,创建 ThreadPoolAutoConfiguration
;
第三步,在 threadpool-spring-boot-starter
工程的 resources 包下创建 META-INF/spring.factories
文件;
最后新建工程引入 threadpool-spring-boot-starter
;
Spring Boot 通过 @EnableAutoConfiguration
开启自动装配,通过 SpringFactoriesLoader 最终加载 META-INF/spring.factories
中的自动配置类实现自动装配,自动配置类其实就是通过 @Conditional
按需加载的配置类,想要其生效必须引入 spring-boot-starter-xxx
包实现起步依赖。