SpringBoot的启动流程分析
一、分析启动类
1、@SpringBootApplication注解
这是一个复合注解,包含了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
(1)@SpringBootConfiguration
@SpringBootConfiguration:继承自@Configuration,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。@SpringBootConfiguration注解类相当于spring配置bean的xml文件。
关于@Configuration,请查看附录
(2)@EnableAutoConfiguration
@EnableAutoConfiguration:是复合注解.
使用@Import将所有符合自动配置条件的bean定义加载到Spring ioc中,并且将所有符合条件的@configuration配置都加载到当前spring ioc。
敢于@Import,请查看附录
(3)@ComponentScan
@ComponentScan:扫描当前包及其子包下被@Component,@Controller,@Service,@Repository注解标记的类并纳入到spring容器中进行管理。
所以SpringBoot的启动类最好放在项目的根目录下。
相当于在xml配置:<context:component-scan>
可以使用@ComponentScan 的basepackage等属性来指定扫描范围。例如:(@SpringBootApplication(scanBasePackages = "com.text.orderservice")
2、SpringApplication.run(XXX.class,args);方法
在创建的main方法中调用SpringApplication.run方法,第一个参数是当前启动类的class,第二个参数是main方法的args。
二、分析SpringApplication类
SpringApplication类的构造函数中调用
//实例化META-INF/spring.factories文件中的ApplicationContextInitializer类
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//实例化META-INF/spring.factories文件中的ApplicationListener类
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
SpringBoot实例化的5个ApplicationContextInitializer
SpringBoot实例化的9个ApplicationListener
1、public ConfigurableApplicationContext run(String… args)
从启动类的run方法点击进入SpringApplication,一直找到这个run方法
public ConfigurableApplicationContext run(String... args) {
//1、创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
//开始记录时间
stopWatch.start();
//2、初始化上下文、异常报告
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//3、设置系统参数 'java.awt.headless'的值,默认值是true
this.configureHeadlessProperty();
//4、创建Spring运行监听器,内部只有一个EventPublishingRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//EventPublishingRunListener会向SpringApplication的所有ApplicationListener广播事件。
//事件有:ApplicationStartedEvent;ApplicationEnvironmentPreparedEvent; //ApplicationPreparedEvent;ApplicationFailedEvent;ApplicationReadyEvent
// 上面分析过,会封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的 // listeners所监听
// 这里接受ApplicationStartedEvent事件的listener会执行相应的操作
listeners.starting();
try {
//5、初始化应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//6、根据运行监听器和应用参数来准备Spring环境
//有StandardServletEnvironment() 和 StandardEnvironment()
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//配置要排除的bean
this.configureIgnoreBeanInfo(environment);
//7、创建Banner打印类
Banner printedBanner = this.printBanner(environment);
//8、创建应用的上下文,
// 有SERVLET 和 REACTIVE 2种,默认是SERVLET
context = this.createApplicationContext();
//9、准备异常报告器,实例化SpringBootExceptionReporter,支持关于启动的错误
this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//10、准备应用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//11、刷新应用上下文
this.refreshContext(context);
//12、上下文创建完成之后执行额外一些操作
this.afterRefresh(context, applicationArguments);
//13、停止计时监控器
stopWatch.stop();
//14、输出日记记录执行主类名、时间信息
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//15、发布应用上下文启动完成事件
listeners.started(context);
//16、执行所有Runner运行期
this.callRunners(context, applicationArguments);
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, exceptionReporters, var9);
throw new IllegalStateException(var9);
}
//17、发布应用上下文就绪事件
listeners.running(context);
//18、返回应用上下文
return context;
}
接下来我们分析run里面的启动内容
2、创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
相关源码:
public void start() throws IllegalStateException {
//当前任务名称,默认为空字符串
this.start("");
}
public void start(String taskName) throws IllegalStateException {
//如果当前已经存在一个任务,抛出异常
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
//设置当前任务名称,并开始记录当前SpringBoot应用启动的开始时间
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
}
3、初始化应用上下文、异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
4、设置java.awt.headless的值
this.configureHeadlessProperty();
java.awt.headless的介绍请查看附录
5、创建Spring运行监听器并发布应用启动时间
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
其中getRunListeners方法是去获取所有的监听器,代码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners的代码如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
其中Set names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
是在调用Spring core 包中的SpringFactoriesLoader.loadFactoryNames方法, 在loadFactoryNames方法中,会找到SpringBoot的jar包下的【META-INF/spring.factories】文件,并读取其中的内容,里面配置了8个监听。
SpringApplicationRunListeners类和SpringApplicationRunListener类的介绍:
SpringApplicationRunListeners内部持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行。
SpringApplicationRunListener用于监听SpringApplication的run方法的执行。
它定义了5个步骤:
1. started(run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent)
2. environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent)
3. contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件)
4. contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent)
5. finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent)
SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener,它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。
所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的
如下图:
附录
一、java.awt.headless模式
1.什么是java.awt.headless模式
java.awt.headless是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。对于后端服务来讲,很多都是需要将这个属性设置为true的。
2.什么时候使用java.awt.headless模式
对于开发者来讲常常需要在该模式下工作。因为服务器(如提供Web服务的)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的、键盘和的主机)。
3.如何使用java.awt.headless模式
文章开头的springboot的源码已经展示了使用方式,就是使用 System.setProperty("java.awt.headless", Boolean headlessFlag);
二、@Configuration
Spring是给予IOC的,在4.0之前的版本,通常都是程序依赖上下文xml文件来管理bean,尽管有了扫描配置后相对简单,然而java配置的方式不同于xml,通过注解能够更简单。
@Configuration,该注解配置在类上,告知Spring这个类是一个拥有bean定义和依赖项的配置类。@Bean注释用于定义Bean,该注解位于实例化bean并设置依赖项的方法上。方法名默认通beanId活默认名称相同,该方法返回类型是Spring注册的bean。总体来说就是告诉Spring容器加载这个配置,相对于xml,这个注解就是将*.xml配置进web.xml
下面我们通过这两种方式比较
- 1、xml中bean的定义
<beans>
<bean id="userController" class="com.test.controller.UserController">
<property name="userService" ref="userService"/>
</bean>
<bean id="userService" class="com.test.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.test.dao.UserDao" />
</beans>
-
2、注解配置类
@Configuration public class AppContext { @Bean public UserController userController() { UserController userController = new UserController(); userController.setUserService(userService()); return userController; } @Bean public UserService userService() { UserService userService = new UserService(); userService.userDao(userDao()); return userService; } @Bean public UserDao userDao() { return new UserDao(); } }
三、@Import
- @Import 与xml配置方式下的 作用一样。支持导入的类型有:
一个或多个拥有 @Configuration 注解的配置类 - ImportSelector 接口的实现类
- ImportBeanDefinitionRegistrar 的实现类
1)、如果Import注解中Class为ImportSelector子类,通过invokeAwareMethods(selector)设置aware值,如果类型为 DeferredImportSelector则添加到deferredImportSelectors集合中,待前面的parser.parse(configCandidates)
方法中processDeferredImportSelectors()处理;如果不是,则执行selectImports方法,将获取到的结果递归调用 processImports,解析selectImports得到的结果
2)、如果Import注解中Class为ImportBeanDefinitionRegistrar子类,则添加到importBeanDefinitionRegistrars中,注 意该部分的数据在执行完parser.parse(configCandidates)后调用this.reader.loadBeanDefinitions(configClasses)解 析,否则执行配置信息的解析操作。
四、@EnableAspectJAutoProxy
表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成
五、@EnableScheduling
是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器