天天看点

springBoot 的启动流程以及相关源码解释

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