3、SpringBoot容器启动整体梳理

Deadly 提交于 2020-02-26 11:11:16

1.1、总体梳理

@SpringBootApplication  
public class SampleTestApplication {  
  
  public static void main(String[] args) {  
      SpringApplication.run(SampleTestApplication.class, args);  
  }  
}

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {  
   return run(new Class<?>[] { primarySource }, args);  
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {  
  // 这里分为两步,创建SpringApplication和run方法,primarySources就是主启动类  
  return new SpringApplication(primarySources).run(args);  
}

1.2、创建SpringApplication

public SpringApplication(Class<?>... primarySources) {  
   this(null, primarySources);  
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {  
  // 默认是null  
  this.resourceLoader = resourceLoader;  
  Assert.notNull(primarySources, "PrimarySources must not be null");  
  // 记录主启动类  
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));  
  // 判断当前应用环境,后续就是根据这创建Spring的IOC容器  
  this.webApplicationType = WebApplicationType.deduceFromClasspath();  
  // 设置初始化器  
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));  
  // 设置监听器  
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));  
  // 确定主配置类  
  this.mainApplicationClass = deduceMainApplicationClass();  
}

static WebApplicationType deduceFromClasspath() {  
  // 从classpath下判断当前SpringBoot应用应该使用哪种环境启动  
  if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)  
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {  
      return WebApplicationType.REACTIVE;  
  }  
  for (String className : SERVLET_INDICATOR_CLASSES) {  
      if (!ClassUtils.isPresent(className, null)) {  
         return WebApplicationType.NONE;  
      }  
  }  
  // 默认返回这个  
  return WebApplicationType.SERVLET;  
}

/**  
 * 源码很简单,从deduceMainApplicationClass方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类  
 */  
private Class<?> deduceMainApplicationClass() {  
   try {  
      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();  
      for (StackTraceElement stackTraceElement : stackTrace) {  
         if ("main".equals(stackTraceElement.getMethodName())) {  
            return Class.forName(stackTraceElement.getClassName());  
         }  
      }  
   }  
   catch (ClassNotFoundException ex) {  
      // Swallow and continue  
  }  
   return null;  
}

1.3、run方法

public ConfigurableApplicationContext run(String... args) {  
  // 创建StopWatch对象,仅用于验证性能. 也就是说,这个组件是用来监控启动时间的  
  StopWatch stopWatch = new StopWatch();  
  stopWatch.start();  
  
  // 创建空的IOC容器  
  ConfigurableApplicationContext context = null;  
  
  // 创建一组空的异常报告器  
  Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();  
  
  // 设置awt相关,设置应用在启动时,即使没有检测到显示器也允许其继续启动.服务器嘛,没显示器照样得运行  
  configureHeadlessProperty();  
  
  // 通过SpringFactoriesLoader.loadFactoryNames获取SpringApplicationRunListener,第2节已经分析过了
  SpringApplicationRunListeners listeners = getRunListeners(args);  
  // 监听器回调,首次启动run方法时立即调用. 可用于非常早期的初始化(准备运行时环境之前)  
  listeners.starting();  
  
  try {  
      // 将运行参数args封装一下  
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
  
      // 环境准备  
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);  
      configureIgnoreBeanInfo(environment);  
  
      // 打印Banner  
      Banner printedBanner = printBanner(environment);  
  
      // 创建IOC容器  
      context = createApplicationContext();  
      // 默认返回的是FailureAnalyzers  
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,  
                                                   new Class[] { ConfigurableApplicationContext.class }, context);  
  
      // 初始化IOC容器  
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
  
      // 刷新容器,beanFactory的预处理,这边就是核心流程,加载创建容器中的bean,后面会单独分析
      refreshContext(context);  
  
      afterRefresh(context, applicationArguments);  
      stopWatch.stop();  
  
      listeners.started(context);  
      // 调用启动加载器
      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;  
}

1.3.1、prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,  
  ApplicationArguments applicationArguments) {  
  // Create and configure the environment  
  // 创建运行时环境  
  ConfigurableEnvironment environment = getOrCreateEnvironment();  
  
  // 配置运行时环境  
  configureEnvironment(environment, applicationArguments.getSourceArgs());  
  ConfigurationPropertySources.attach(environment);  
  // 监听器回调,SpringApplicationRunListener的environmentPrepared方法(Environment构建完成,但在创建ApplicationContext之前)  
  listeners.environmentPrepared(environment);  
  // 环境与应用绑定  
  bindToSpringApplication(environment);  
  if (!this.isCustomEnvironment) {  
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,  
                                                                                             deduceEnvironmentClass());  
  }  
  ConfigurationPropertySources.attach(environment);  
  return environment;  
}

private ConfigurableEnvironment getOrCreateEnvironment() {  
  if (this.environment != null) {  
      return this.environment;  
  }  
  switch (this.webApplicationType) {  
  case SERVLET:  
      // 默认创建这个环境  
      return new StandardServletEnvironment();  
  case REACTIVE:  
      return new StandardReactiveWebEnvironment();  
  default:  
      return new StandardEnvironment();  
  }  
}

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {  
   // 默认是true  
  if (this.addConversionService) {  
      // 向容器中添加一个类型转换器,而且是共享的,线程安全的.实际上就是它在SpringWebMvc中做参数类型转换  
      ConversionService conversionService = ApplicationConversionService.getSharedInstance();  
      environment.setConversionService((ConfigurableConversionService) conversionService);  
  }  
  configurePropertySources(environment, args);  
  configureProfiles(environment, args);  
}

1.3.2、打印banner

// 打印Banner  
Banner printedBanner = printBanner(environment);

private Banner printBanner(ConfigurableEnvironment environment) {  
  // banner打印模式,默认是控制台打印  
  if (this.bannerMode == Banner.Mode.OFF) {  
      return null;  
  }  
  // this.resourceLoader默认为null,这边创建一个,用于加载资源  
  // ResourceLoader用于加载资源的策略接口(例如从类路径或文件系统资源)  
  ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : 
                                                                  new DefaultResourceLoader(getClassLoader());  
  // 使用BannerPrinter打印Banner,this.banner默认也是null  
  SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);  
  if (this.bannerMode == Mode.LOG) {  
      return bannerPrinter.print(environment, this.mainApplicationClass, logger);  
  }  
  // 默认走这边  
  return bannerPrinter.print(environment, this.mainApplicationClass, System.out);  
}

public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {  
  // 获取Banner  
  Banner banner = getBanner(environment);  
  // 打印Banner  
  banner.printBanner(environment, sourceClass, out);  
  // 最后把Banner封装成PrintedBanner返回  
  return new PrintedBanner(banner, sourceClass);  
}

private Banner getBanner(Environment environment) {  
  Banners banners = new Banners();  
  // 先加载图片Banner和文字Banner  
  banners.addIfNotNull(getImageBanner(environment));  
  banners.addIfNotNull(getTextBanner(environment));  
  // 只要有一个就返回  
  if (banners.hasAtLeastOneBanner()) {  
      return banners;  
  }  
  if (this.fallbackBanner != null) {  
      return this.fallbackBanner;  
  }  
  // 默认实现是SpringBootBanner  
  return DEFAULT_BANNER;  
}
class SpringBootBanner implements Banner {  
  
   private static final String\[\] BANNER = { "", "  .   ____          _            __ _ _",  
      " /\\\\\\ / ___'_ __ _ _(_)_ __  __ _ \\\ \\\ \\\ \\\", "( ( )\\\___ | '_ | '_| | '_ \\\/ _` | \\\ \\\ \\\ \\\",  
      " \\\\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\\__, | / / / /",  
     " =========|_|==============|___/=/_/_/_/" };  
  
   private static final String SPRING_BOOT = " :: Spring Boot :: ";  
  
   private static final int STRAP\_LINE\_SIZE = 42;  
  
   @Override  
   public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {  
      for (String line : BANNER) {  
         printStream.println(line);  
      }  
      String version = SpringBootVersion.getVersion();  
      version = (version != null) ? " (v" \+ version + ")" : "";  
      StringBuilder padding = new StringBuilder();  
      while (padding.length() < STRAP\_LINE\_SIZE \- (version.length() + SPRING_BOOT.length())) {  
         padding.append(" ");  
      }  
  
      printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),  
                                                                                       AnsiStyle.FAINT, version));  
      printStream.println();  
  }  
  
}
// 可以指定文字banner的位置
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";  

// 可以指定图片banner的位置
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";  

// 在resources目录下新建banner.txt文件,里面可以指定banner的输出格式
static final String DEFAULT_BANNER_LOCATION = "banner.txt";  
  
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };

1.3.3、创建IOC容器

// 创建IOC容器  
context = createApplicationContext();

protected ConfigurableApplicationContext createApplicationContext() {  
   Class<?> contextClass = this.applicationContextClass;  
   if (contextClass == null) {  
      try {  
         // 根据Web应用类型决定实例化哪个IOC容器  
         switch (this.webApplicationType) {  
             case SERVLET:  
                // 默认是创建这个容器  
                contextClass = Class.forName(DEFAULT\_SERVLET\_WEB\_CONTEXT\_CLASS);  
                break; 
             case REACTIVE:  
                contextClass = Class.forName(DEFAULT\_REACTIVE\_WEB\_CONTEXT\_CLASS);  
                break; 
             default:  
                contextClass = Class.forName(DEFAULT\_CONTEXT\_CLASS);  
         }  
      } catch (ClassNotFoundException ex) {  
          // 抛异常
      }  
   }  
   // 通过反射创建IOC容器,在这里beanFactory就被创建出来了  
   return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);  
}

1.3.4、prepareContext

// 初始化IOC容器  
prepareContext(context, environment, listeners, applicationArguments, printedBanner);

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,  
                            SpringApplicationRunListeners listeners, 
			    ApplicationArguments applicationArguments, Banner printedBanner) {  
  
  // 将创建好的应用环境设置到IOC容器中  
  context.setEnvironment(environment);  
  // IOC容器的后置处理  
  postProcessApplicationContext(context);  
  // 执行Initializer  
  applyInitializers(context);  
  // 回调SpringApplicationRunListeners的contextPrepared方法(在创建和准备ApplicationContext之后,但在加载之前)  
  listeners.contextPrepared(context);  
 
  // Add boot specific singleton beans  
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();  
  // 注册两个组件,1个是Main方法的参数、还有一个是打印banner的  
  beanFactory.registerSingleton("springApplicationArguments", applicationArguments);  
  if (printedBanner != null) {  
      beanFactory.registerSingleton("springBootBanner", printedBanner);  
  }  
  if (beanFactory instanceof DefaultListableBeanFactory) {  
      // 默认是false  
      ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);  
  }  
  // Load the sources  
  // 加载主启动类  
  Set<Object> sources = getAllSources();  
  Assert.notEmpty(sources, "Sources must not be empty");  
  // 注册主启动类  
  load(context, sources.toArray(new Object\[0\]));  
  listeners.contextLoaded(context);  
}

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {  
  // 注册BeanName生成器,默认是null  
  if (this.beanNameGenerator != null) {  
      context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,  
                                                 this.beanNameGenerator);  
  }  
  // 设置资源加载器和类加载器,默认是null  
  if (this.resourceLoader != null) {  
      if (context instanceof GenericApplicationContext) {  
         ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);  
      }  
      if (context instanceof DefaultResourceLoader) {  
         ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());  
      }  
  }  
  // 设置类型转换器  
  if (this.addConversionService) {  
      context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());  
  }  
}

protected void applyInitializers(ConfigurableApplicationContext context) {  
   for (ApplicationContextInitializer initializer : getInitializers()) {  
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),  
                                                                      ApplicationContextInitializer.class);  
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");  
      initializer.initialize(context);  
  }  
}

public Set<Object> getAllSources() {  
    Set<Object> allSources = new LinkedHashSet<>();  
    // 这就是之前赋值过的主启动类  
    if (!CollectionUtils.isEmpty(this.primarySources)) {  
        allSources.addAll(this.primarySources);  
    }  
    // 默认为空  
    if (!CollectionUtils.isEmpty(this.sources)) {  
        allSources.addAll(this.sources);  
    }  
    return Collections.unmodifiableSet(allSources);  
}

protected void load(ApplicationContext context, Object\[\] sources) {  
    // 先获取BeanDefinitionRegistry,获取的实际就是IOC容器  
    // 进去分析下创建方法,发现创建了几个关键的组件  
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);  
    // 设置beanName生成器,debug发现此时还没有注册  
    if (this.beanNameGenerator != null) {  
        loader.setBeanNameGenerator(this.beanNameGenerator);  
    }  
    // 为null  
    if (this.resourceLoader != null) {  
        loader.setResourceLoader(this.resourceLoader);  
    }  
     // 为null  
    if (this.environment != null) {  
        loader.setEnvironment(this.environment);  
    }  
     // 这边分析了下好像就是讲主启动类作为bean注册到了BeanDefinition中  
    loader.load();  
}

1.3.5、callRunners

如果要在SpringBoot框架一启动完成就做某件事情,可以利用启动加载器
// 启动加载器的实现方式1,同等Order下ApplicationRunner的run方法先执行
@Component  
@Order(1)  
public class FirstApplicationRunner implements ApplicationRunner {  
  
    @Override  
    public void run(ApplicationArguments args) throws Exception {  
        System.out.println("FirstApplicationRunner run");  
    }  
}
// 启动加载器的实现方式2
@Component  
@Order(1)  
public class FirstCommandLineRunner implements CommandLineRunner {  
  
    @Override  
    public void run(String... args) throws Exception {  
        System.out.println("FirstCommandLineRunner run");  
    }  
}
// 调用启动加载器,在容器启动完成之后做一些事情  
callRunners(context, applicationArguments);

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());  
    AnnotationAwareOrderComparator.sort(runners);  
    for (Object runner : new LinkedHashSet<>(runners)) {  
        if (runner instanceof ApplicationRunner) {  
           callRunner((ApplicationRunner) runner, args);  
        }  
        if (runner instanceof CommandLineRunner) {  
           callRunner((CommandLineRunner) runner, args);  
        }  
    }  
}  
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!