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<String> 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容器