如何理解SpringBoot starters的自动装配
一、什么是 SpringBoot 自动装配?
Spring Framework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
没有 SpringBoot 情况下,如果需要引入外部依赖,需要手动依赖 jar 包。在SpringBoot 中,可以直接引入一个 starter 即可。如果想在项目中使用 json 依赖,直接在项目中引入对应的 starter 即可,引入之后,使用一些少量的注解和简单的配置就能使用引入的功能了。
org.springframework.boot spring-boot-starter-json
简单来说就是,引入依赖告诉 SpringBoot 要使用什么,SpringBoot 会通过注解和少量配置完成功能实现。
二、SpringBoot 的自动装配以及按需加载
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringBootDemo3Application { public static void main(String[] args) { SpringApplication.run(SpringBootDemo3Application.class, args); }}
SpringBoot 启动类上的注解 @SpringBootApplication ,可以看成是
@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制;
@ComponentScan:扫描被 @Component ( @Service, @Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean 的组合
@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 { ...}
从名字中可以看出,@EnableAutoConfiguration 这个注解是自动装配的核心注解,但是其核心功能还是使用 AutoConfigurationImportSelector 加载自动装配类实现的,在下面的第16行代码可以看到,@Import 该类。
package org.springframework.boot.autoconfigure;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.context.annotation.Import;@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 {};}
AutoConfigurationImportSelector 的继承体系如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}public interface DeferredImportSelector extends ImportSelector {}public interface ImportSelector { String[] selectImports(AnnotationMetadata var1);}
AutoConfigurationImportSelector 实现了接口 ImportSelector 中的 selectImports 方法,此方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IOC 容器中
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }}
此处的 getAutoConfigurationEntry() 方法负责加载自动配置类
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); }}
if (!this.isEnabled(annotationMetadata)) 判断自动装配是否打开。默认 spring.boot.enableautoconfiguration=true,可在application.properties 或 application.yml 中进行配置。
AnnotationAttributes attributes = this.getAttributes(annotationMetadata); 获取 @EnableAutoConfiguration 注解中的 exclude 和 excludeName ,取消这些内容的装配。
configurations = this.removeDuplicates(configurations); 获取需要自动装配的所有配置类 ,读取 META-INF/spring.factories 文件。
此时,所有的配置文件都被读取到,可以看到 configurations 的大小为148
可以看到这个文件的配置内容都被读取到了,不光是这个依赖下的 META-INF/spring.factories 被读取到,所有 Spring Boot Starter 下的 META-INF/spring.factories 都会被读取到
并不是所有配置在 SpringBoot 启动时都会加载,
此处 configurations 的大小变成了27,这一步经历了一遍筛选,完成按需加载。