@SpringBootApplication实际上是一个复合注解,其源码中包含如下注解:
@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) })
其中最重要的是后三个注解:@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan,对前几个的注解仅稍作介绍。
@Target(ElementType.TYPE)
Target注解为元注解,指明被修饰注解的修饰对象,分为TYPE(描述类、接口(包括注解类型) 或enum声明)、FIELD(描述域)、METHOD(描述方法)、PARAMETER(描述方法)、CONSTRUCTOR(描述构造器)、LOCAL_VARIABLE(描述局部变量)、ANNOTATION_TYPE(描述注解)、PACKAGE(描述包)、TYPE_PARAMETER(描述类型参数)和TYPE_USE(描述任何参数)
@Retention(RetentionPolicy.RUNTIME)
元注解,指明被修饰注解的生命周期,分为RetentionPolicy.SOURCE-注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;RetentionPolicy.CLASS-注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;以及RetentionPolicy.RUNTIME-注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
@Documented
@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
@Inherited
被修饰的类型是自动继承的,更具体地说,如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中。
接下来的三个注解是复合注解@SpringBootApplication的核心功能:
@SpringBootConfiguration
@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。SpringBootConfiguration注解实际上标明该启动类同时也是一个 IoC 容器的配置类。
@EnableAutoConfiguration
@EnableAutoConfiguration与其他Spring中Enable作为前缀的注解相似,都是利@Import收集和注册特定场景相关的 bean 定义到IOC容器,其源码中相关代码为:
@Import(AutoConfigurationImportSelector.class)
借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。核心方法如下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
其中的SpringFactoriesLoader 是 Spring 框架私有的一种扩展方案,在此处的作用是根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key,获取对应的一组 @Configuration 类名。
最终,@EnableAutoConfiguration 即完成了如下工作:从 classpath 中搜寻所有 META-INF/spring.factories 配置文件,并将其中 org.spring-framework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射(Java Reflection)实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。以上过程与所有Spring boot starter发挥作用密切相关。
各种官方或自定义的Spring Boot starter能够极大地简化拓展模块的配置过程,只需在Spring Boot项目的pox文件内添加相应依赖就能完成各种繁琐的配置工作。其核心为jar包中被@EnableAutoConfiguration 修饰的自动装配类:
@Configuration
@EnableConfigurationProperties(StarterTestProperties.class)
public class StarterAutoConfigure {
@Autowired
private StarterTestProperties properties;
@Bean
StarterService starterService (){
return new StarterService(properties.getConfig());
}
}
@EnableConfigurationProperties表示激活各种配置文件读取类,配置文件读取类一般命名为XxxProperties,该配置类主要用于接收Spring boot中application.properties或者application.yml的配置项,主要代码如下:
@ConfigurationProperties(prefix = "starter.test")
public class StarterTestProperties {
private String config;
public void setConfig(String config) {
this.config = config;
}
public String getConfig() {
return config;
}
}
在拥有多个配置文件读取类和一个自动装配类后,再在resources/META-INF/下创建spring.factories文件,并指明自动装配类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.autocinfigure.StarterAutoConfigure
如此一来,整个扩展模块的配置信息便可以通过前文所述的SpringFactoriesLoader在项目启动的时候被加载进来。
@ComponentScan
@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
可以看到ComponentScan配置了2个excludeFilters,我们可以通过继承TypeExcludeFilter类或者AutoConfigurationExcludeFilter类并实现match()来排除我们不想扫描到的类。