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三个注解构成。
- ComponentScan 注解的作用是扫描启动类所在的包以及子包所有Bean组件并注册到 IOC 容器中,其中 excludeFilters 指定了一个过滤器列表,通过两个过滤器排除某些类,比如我们可以继承 TypeExcludeFilter 并重写 match 方法来自定义给排除哪些类。
- SpringConfiguration 注解的作用就是标记 SpringBoot 启动类为一个配置类。
- 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 的步骤
-
创建 Spring Boot 工程,添加项目需要的依赖,Spring Configuration Processor 这个依赖可以帮助开发者自动生成配置的的代码提示。
-
删除 pom.xml 的 build 部分
-
创建 Config 文件
-
在 resources 文件夹下新建 META-INF 文件夹,然后新建 spring.factories 文件,编写配置项为自动引入配置的类。
-
之后可以对这个项目打包,在其他的项目中通过pom.xml文件的坐标引入这个项目
-
在 AutoConfigurationImportSelector 中也可以发现com.example.demo.DemoConfiguration 类已经被成功加载了。