SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。
我们开发任何一个Spring Boot项目,都会用到如下的启动类
-
@SpringBootApplication
-
publicclassApplication {
-
publicstaticvoid main(String[] args) {
-
SpringApplication.run(Application.class, args);
-
}
-
}
从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以要揭开SpringBoot的神秘面纱,我们要从这两位开始就可以了。
一、SpringBootApplication背后的秘密
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:
-
@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@interfaceSpringBootApplication {
-
...
-
}
虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:
@Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
即 @SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等:
-
@Configuration
-
@EnableAutoConfiguration
-
@ComponentScan
-
publicclassApplication {
-
publicstaticvoid main(String[] args) {
-
SpringApplication.run(Application.class, args);
-
}
-
}
每次写这3个比较累,所以写一个@SpringBootApplication方便点。接下来分别介绍这3个Annotation。
1、@Configuration
(1)表达形式层面
基于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
-
publicclassMockConfiguration{
-
//bean定义
-
}
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。
(2)注册bean定义层面
基于XML的配置形式是这样:
-
<bean id="mockService"class="..MockServiceImpl">
-
...
-
</bean>
而基于JavaConfig的配置形式是这样的:
-
@Configuration
-
publicclassMockConfiguration{
-
@Bean
-
publicMockService mockService(){
-
returnnewMockServiceImpl();
-
}
-
}
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
(3)表达依赖注入关系层面
为了表达bean与bean之间的依赖关系,在XML形式中一般是这样:
-
<bean id="mockService"class="..MockServiceImpl">
-
<propery name ="dependencyService"ref="dependencyService" />
-
</bean>
-
-
<bean id="dependencyService"class="DependencyServiceImpl"></bean>
而基于JavaConfig的配置形式是这样的:
-
@Configuration
-
publicclassMockConfiguration{
-
@Bean
-
publicMockService mockService(){
-
returnnewMockServiceImpl(dependencyService());
-
}
-
-
@Bean
-
publicDependencyService dependencyService(){
-
returnnewDependencyServiceImpl();
-
}
-
}
如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。
@Configuration:提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
-
<beans>
-
<bean id = "car"class="com.test.Car">
-
<property name="wheel"ref = "wheel"></property>
-
</bean>
-
<bean id = "wheel"class="com.test.Wheel"></bean>
-
</beans>
相当于:
-
@Configuration
-
publicclassConf {
-
@Bean
-
publicCar car() {
-
Car car = newCar();
-
car.setWheel(wheel());
-
return car;
-
}
-
-
@Bean
-
publicWheel wheel() {
-
returnnewWheel();
-
}
-
}
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
2、@ComponentScan
@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
3、@EnableAutoConfiguration
@EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。
@EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。
而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,仅此而已!
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:
-
@SuppressWarnings("deprecation")
-
@Target(ElementType.TYPE)
-
@Retention(RetentionPolicy.RUNTIME)
-
@Documented
-
@Inherited
-
@AutoConfigurationPackage
-
@Import(EnableAutoConfigurationImportSelector.class)
-
public@interfaceEnableAutoConfiguration {
-
...
-
}
自动配置幕后英雄:SpringFactoriesLoader详解
SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。
-
publicabstractclassSpringFactoriesLoader {
-
//...
-
publicstatic <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
-
...
-
}
-
publicstaticList<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
-
....
-
}
-
}
配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。
上图就是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容,可以很好地说明问题。
二、深入探索SpringApplication执行流程
SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
去除事件通知点后,整个流程如下:
本文以调试一个实际的SpringBoot启动程序为例,参考流程中主要类类图,来分析其启动逻辑和自动化配置原理。
总览
启动
@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置。
SpringBoot启动类
首先进入run方法
run方法中去创建了一个SpringApplication实例,在该构造方法内,我们可以发现其调用了一个初始化的initialize方法
这里主要是为SpringApplication对象赋一些初值。构造函数执行完毕后,我们回到run方法
该方法中实现了如下几个关键步骤:
1、创建了应用的监听器SpringApplicationRunListeners并开始监听
3、配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
4、创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文),我们可以看一下创建方法:
ConfigurableApplicationContext类图如下:
主要看其继承的两个方向:
LifeCycle:生命周期类,定义了start启动、stop结束、isRunning是否运行中等生命周期空值方法
ApplicationContext:应用上下文类,其主要继承了beanFactory(bean的工厂类)
5、回到run方法内,prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
自动化配置
之前的启动结构图中,我们注意到无论是应用初始化还是具体的执行过程,都调用了SpringBoot自动配置模块。
SpringBoot自动配置模块
工厂接口与其若干实现类接口名称
下图有助于我们形象理解自动配置流程。
SpringBoot自动化配置关键组件关系图
之前我们提到了EnableAutoConfiguration注解,其类图如下:
该方法中的getCandidateConfigurations方法,通过方法注释了解到,其返回一个自动配置类的类名列表,方法调用了loadFactoryNames方法,查看该方法
进入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中,主要看一下类头:
发现Spring的@Configuration,俨然是一个通过注解标注的springBean,继续向下看,
@CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource。
这里是截取的mybatis-spring-boot-starter的源码中pom.xml文件中所有依赖: