Springboot的启动类可以是非常简单,其中最关键的两部分是Annotation定义( @SpringBootApplication )和类定义(SpringApplication.run),这篇文章主要分析其 @SpringBootApplication 注解,后续文章再接着分析其类定义。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
其中参考了《SpringBoot揭秘》里面的分析,也是学习总结了。
@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 {
.....
}
复制代码
看似有这么多注解,实际上 @SpringBootApplication 是一个"三体"结构,重要的只有三个Annotation:
@Configuration @EnableAutoConfiguration @ComponentScan
为什么 @SpringBootApplication注解 里没有包含 @Configuration ,实际上是在 @SpringBootConfiguration 里面
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
复制代码
实际上如果我们使用如下的Springboot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等。
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
复制代码
下面分别说说这三个关键注解
这里的 @Configuration 就是以JavaConfig形式的Spring IoC容器的配置类使用的那个 @Configuration ,所以这里的启动类标注了 @Configuration 之后,本身其实也是一个IoC容器的配置类。
以前的XML配置是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
复制代码
现在的JavaConfig配置是这样的,效果等同,标注了该注解说明就是一个配置类
@Configuration
public class Application{
//bean定义
}
复制代码
更多关于 @Configuration 和 @Bean 的讲解,参考文章: 使用 Java 配置进行 Spring bean 管理
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器中。 我们可以通过basePackages等属性指定@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现从声明@ComponentScan所在类的package进行扫描,默认情况下是不指定的,所以SpringBoot的启动类最好放在root package下。
各位是否还记得Spring框架提供的各种名字为@Enable开头的Annotation定义?比如 @EnableScheduling , @EnableCaching , @EnableMBeanExport 等, @EnableAutoConfiguration 的理念和"做事方式"其实一脉相承, 借助 @Import 的支持,收集和注册特定场景相关的bean定义:
@EnableScheduling @EnableMBeanExport
而@EnableAutoConfiguration也是借助@Import的帮助, 将所有符合自动配置条件的bean定义加载到Ioc容器,仅此而已
@EnableAutoConfiguration也是一个复合Annotation,其定义如下:
@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 {};
}
复制代码
其中,最关键的要属 @Import(AutoConfigurationImportSelector.class) ,借助 AutoConfigurationImportSelector 这个类, @EnableAutoConfiguration 可以帮助Springboot应用将所有符合条件的@configuration都加载到当前的SpringBoot创建并使用的Ioc容器
在SpringBoot1.5以前,使用的是 EnableAutoConfigurationImportSelector ,它继承自 AutoConfigurationImportSelector ,1.5以后, EnableAutoConfigurationImportSelector 已经不再被建议使用,而是推荐使用 AutoConfigurationImportSelector 。
现在我们已经知道了在@EnableAutoConfiguration中引入了AutoConfigurationImportSelector类,那么它是如何被执行的呢?
Springboot启动时会使用ConfigurationClassParser来解析被@Configuration修饰的配置类,然后再处理这个类内部被其他注解修饰的情况,比如@Import注解,@ComponentScan注解,@Bean注解等。
如果发现注解中存在@Import(ImportSelector)的情况下,就会创建一个相应的importSelector对象,并调用其selectImports方法,而AutoConfigurationImportSelector就是一个ImportSelector的实现类。更多关于ConfigurationClassParser的分析,参阅文章:Spring类注册笔记
所以ConfigurationClassParser会实例化一个AutoConfigurationImportSelector 并调用它的 selectImports() 方法
在selectImports方法中有使用getCandidateConfigurations()这个方法,这个方法走进去,就可以看到自动配置的幕后英雄:SpringFactoriesLoader
SpringFactoriesLoader的主要功能就是从指定的配置文件 META/spring.factories 加载配置,spring.factories是一个典型的java properties文件,配置格式为Key-Value形式,只不过Key和Value都是Java类型的完整类名。
进入loadFactoryNames()方法,就发现loadFactoryNames()读取了ClassPath下面的 META-INF/spring.factories 文件。
在@EnableAutoConfiguration的场景中,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的key,获取对应的一组@Configuration类
@SpringBootApplication不仅包括上面的三个重要注解,还包含有4个方法:
Class<?>[] exclude() default {};
String[] excludeName() default {};
String[] scanBasePackages() default {};
Class<?>[] scanBasePackageClasses() default {};
这里总结下@SpringBootApplication中的三个重要注解的特征:
@Configuration 定义Spring Ioc容器的配置类
@EnableAutoConfiguration : 从classpath中搜寻所有META/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项,也就是一个自动配置类列表加载到Ioc容器中。 而对于所有标注@Configuration的配置类,统一使用 ConfigurationClassParser 解析的。
@ComponentScan 自动扫描并加载符合条件的组件或者bean定义