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 类已经被成功加载了。

相关推荐
吾日三省吾码4 小时前
JVM 性能调优
java
Estar.Lee4 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐5 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi775 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610036 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3436 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀6 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20206 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深6 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++