spring boot是一个基于spring framework之上的应用构建框架,遵循COC原则,帮助我们快速构建spring应用,这也是spring boot名称由来吧。
那spring boot是通过什么魔法能够帮助我们快速构建spring应用呢?让我们一起来一趟究竟。
通常一个spring boot启动程序代码如下:
@SpringBootApplication
class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
对上述代码唯一感到新奇的就是SpringBootApplication注解和SpringApplication的run方法。那就让我们直接撸源码吧。
SpringBootApplication注解
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) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
通过定义可以看到SpringBootApplication是一个组合注解,其标注了
SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个元注解,通过我们对spring framework的了解,我们可以知道,SpringBootApplication相当于可以被替换成这样:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
其中SpringBootConfiguration其实就是相当于Configuration,ComponentScan这个注解大家都很熟悉,唯一不怎么熟悉的就是EnableAutoConfiguration注解,其实在spring framework中有很多Enable开头的注解,比如EnableScheduling、EnableAsync,EnableAutoConfiguration注解也没有什么神秘之处,原理跟他们Enable开头的其他注解类似,通过在注解上标注@Import引入其他Bean。有关EnableAutoConfiguration注解后续还会提到。下面先来看看SpringApplication这个类的实现。
SpringApplication
SpringApplication中的代码并不多,但是有一些细枝末节,这里先列举一1下其中的主要流程:
-
main方法调用SpringApplication.run方法
- SpringApplication实例初始化流程:
2.1 ApplicationContextInitializer用于初始化ApplicationContext,其会在ApplicationContext.refresh方法之前运行,spring boot默认会加载SharedMetadataReaderFactoryContextInitializer和AutoConfigurationReportLoggingInitializer两个容器初始化器。
2.2 ApplicationListener是spring framework的容器事件监听器,spring boot默认加载BackgroundPreinitializer监听器,用于监听容器环境已经准备好事件(ApplicationEnvironmentPreparedEvent)。 -
SpringApplication.run流程:
图中标注的红色部分表示这一步非常重要.
其实SpringApplication的run方法提供了很多的扩展点,比如说SpringApplicationRunListener用于监听容器启动的各个阶段,SpringContextInitializer用于初始化容器,如果去除这些扩展点run方法的执行流程就相当简单了。另外run方法会根据配置决定是否打印banner。
条件配置
spring 4.x引入了@Conditional注解,基于某些特定条件决定是否某些bean的配置是否生效,在4.x之前为了达到这个目的一般采用spring el或者profile的方式来选择bean是否生效。在spring boot中扩展了许多条件注解,例如:
- @ConditionalOnClass 类路径存在某个类
- @ConditionalOnMissingBean 容器中不存在某个bean
- @ConditionalOnBean 容器中存在某个bean
- @ConditionalOnProperty 配置文件(properties/yml)存在某个属性
除了基于这些条件配置,我们还可以调整bean或者组件的加载顺序. - @AutoConfigureBefore 在某个配置之前执行
- @AutoConfigureAfter 在某个配置之后执行