Spring Boot 自动装配原理及 Starter 实现

1、Situation

传统 Spring 引入依赖时需要用 XML 或 Java 显式配置,非常繁琐。

2、Target

方便快捷地引入依赖或者配置属性。

3、Action

3.1 @SpringBootApplication源码解析

java 复制代码
@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 {};
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};
	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}
--------------------------------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
-------------------------------------------------------
@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 {};

}

主要有SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个注解构成。

  1. ComponentScan 注解的作用是扫描启动类所在的包以及子包所有Bean组件并注册到 IOC 容器中,其中 excludeFilters 指定了一个过滤器列表,通过两个过滤器排除某些类,比如我们可以继承 TypeExcludeFilter 并重写 match 方法来自定义给排除哪些类。
  2. SpringConfiguration 注解的作用就是标记 SpringBoot 启动类为一个配置类。
  3. EnableAutoConfiguration 是实现自动装配的核心注解,通过@Import 注解导入 AutoConfigurationImportSelector 类

3.2 AutoConfigurationImportSelector 源码

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		//1.判断自动装配开关是否打开
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		//2.获取所有需要装配的bean
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		//1.再次判断自动装配开关是否打开
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//2.获取EnableAutoConfiguration注解中的 exclude 和 excludeName
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//3.获取需要自动装配的所有配置类,读取META-INF/spring.factories
		//(不止读取当前项目中的META-INF文件,所有的依赖中的META-INF文件都会被读取)
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//4.删除重复依赖、过滤 exclude 的依赖
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
	...
}
------------------------------------------------------
public interface DeferredImportSelector extends ImportSelector {

}
-------------------------------------------------------
public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

AutoConfigurationImportSelector 实现了 ImportSelector 的selectImports 方法,顾名思义就是筛选引入的依赖,那么就需要加载所有的依赖,以及条件,再根据条件对依赖进行筛选。

4、Result

通过 Spring Boot 启动类上的 @SpringBootApplication 注解,Spring 就可以遍历所有 META-INF 的 spring.factories 文件中的配置类,并根据 @EnableAutoConfiguration 的条件选择是否将其注册为 bean。

5、面试回答

启动类的@SpringBootApplication注解由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,三个注解共同完成自动装配;

  • @SpringBootConfiguration 注解标记启动类为配置类
  • @ComponentScan 注解实现启动时扫描启动类所在的包以及子包下所有标记为bean的类由IOC容器注册为bean
  • @EnableAutoConfiguration 通过 @Import 注解导入
    AutoConfigurationImportSelector类,然后通过AutoConfigurationImportSelector 类的 selectImports
    方法去读取需要被自动装配的组件依赖下的spring.factories文件配置的组件的类全名,并按照一定的规则过滤掉不符合要求的组件的类全名,将剩余读取到的各个组件的类全名集合返回给IOC容器并将这些组件注册为bean。

6、实现 Starter 的步骤

  1. 创建 Spring Boot 工程,添加项目需要的依赖,Spring Configuration Processor 这个依赖可以帮助开发者自动生成配置的的代码提示。

  2. 删除 pom.xml 的 build 部分

  3. 创建 Config 文件

  4. 在 resources 文件夹下新建 META-INF 文件夹,然后新建 spring.factories 文件,编写配置项为自动引入配置的类。

  5. 之后可以对这个项目打包,在其他的项目中通过pom.xml文件的坐标引入这个项目

  6. 在 AutoConfigurationImportSelector 中也可以发现com.example.demo.DemoConfiguration 类已经被成功加载了。

相关推荐
silence25011 分钟前
深入了解 Reactor:响应式编程的利器
java·spring
weixin_SAG21 分钟前
21天掌握javaweb-->第19天:Spring Boot后端优化与部署
java·spring boot·后端
m0_7482475524 分钟前
SpringMVC跨域问题解决方案
java
Elcker25 分钟前
KOI技术-事件驱动编程(Sping后端)
java·spring·架构
GitNohup28 分钟前
Spring boot处理跨域问题
java·spring boot·跨域
Just_Paranoid39 分钟前
使用 IDE生成 Java Doc
java·开发语言·ide
西海天际蔚蓝1 小时前
递归查询全量分页数据问题
java
SomeB1oody1 小时前
【Rust自学】7.4. use关键字 Pt.2 :重导入与换国内镜像源教程
开发语言·后端·rust
新知图书1 小时前
Rust编程与项目实战-箱
开发语言·后端·rust
俎树振1 小时前
深入理解与优化Java二维数组:从定义到性能提升的全面指南
java·算法