springboot启动流程分析(一)

以springboot 2.0.2.RELEASE版本为例

1.pom.xml引入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

2.查看启动入口类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringdemoApplication {
    public static void main(String[] args) {
        System.out.println("Service start"); // 自己添加的标记,最开始执行
        SpringApplication.run(SpringdemoApplication.class, args);
        System.out.println("Service end");   // 自己添加的标记,最后执行
    }
}

3.查看@SpringBootApplication注解

// 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Target(ElementType.TYPE)
// 注解的生命周期,保留到class文件中(三个生命周期)
@Retention(RetentionPolicy.RUNTIME)
// 表明这个注解应该被javadoc记录
@Documented
// 子类可以继承该注解
@Inherited
// 继承了Configuration,表示当前是注解类
@SpringBootConfiguration
// 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@EnableAutoConfiguration
// 扫描路径设置(具体使用待确认)
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

4.执行main入口函数,首先会打印之前添加的:”Service start”
5.进入该函数运行:

// 会有两个参数:
// 参数1:初始化一个SpringApplication对象,将入口类作为springboot的初始化参数
// 参数2:args入参,不设置的时候为空:{}
SpringApplication.run(SpringdemoApplication.class, args);

6.进入该run函数,如下:

// (1)、入口run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    // args={},primarySource=SpringdemoApplication.class
    // (2)将primarySource封装为数组传递,调用下一个run方法
    return run(new Class[]{primarySource}, args);
}
// (3)下一个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    (4)调用SpringApplication的构造函数实例化,实例化后再执行该类的run方法
    return (new SpringApplication(primarySources)).run(args);
}

7.查看SpringApplication的构造函数:

public SpringApplication(Class<?>... primarySources) {
        // this为调用本类中的另一个构造函数
        this(null, primarySources);
    }

8.另一个构造函数如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;   // 传入为空
   // 判断是否有入口类
   Assert.notNull(primarySources, "PrimarySources must not be null");
   // 转化为元素插入的顺序来维护集合的链接表
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   // 推断当前环境是什么环境:Servlet、Reactive、或者不是web环境,判断逻辑是classpath中的类?????? 比如win10下这个值是SERVELET 
   this.webApplicationType = deduceWebApplicationType();
   // 初始化器initializers:这些初始化器(initializers)是Spring Boot通过读取每个jar包下的/META-INF/spring.factories文件中的配置获取的。每一个initailizer都是一个实现了ApplicationContextInitializer接口的实例。ApplicationContextInitializer是Spring IOC(控制反转)容器中提供的一个接口:
   //【特别要注意的是,这块的扫描结果,是为了run方法中的prepareContext和refreshContext函数初始化的,参见:https://www.cnblogs.com/hzhuxin/p/7742365.html】
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   // 监听器listeners:从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器 
   // 【参见:https://blog.csdn.net/Bob_666/article/details/79715156】
   setListeners((Collection)
   // 该方法获取执行类型子类实例集合,不太明白???????????????? 
   getSpringFactoriesInstances(ApplicationListener.class));
   // 获取当前调用栈,找到入口方法main所在的类,并将其复制给SpringApplication对象的成员变量mainApplicationClass
   this.mainApplicationClass = deduceMainApplicationClass();
}

9.开始执行run方法【具体的run方法,待后续】:

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();# 任务时间监听器启动
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      // 核心点:会打印springboot的启动标志,直到server.port端口启动
      refreshContext(context); 
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      // 会调用各种Runners进行操作
      callRunners(context, applicationArguments); 
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

10.Runners操作:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   // 这里的时候,runners就会得到各种实现了CommandLineRunner的类
   AnnotationAwareOrderComparator.sort(runners); # 会根据order对runner排序
   // 依次遍历runner,并将其中的值取出来,执行run方法
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
} # 这里runner实际上是为用户提供了两种开机后启动的程序代码

其它资源参见:http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow 【未完待续,待分析】

相关文章
相关标签/搜索