Spring Boot 2.0系列文章(七):SpringApplication 深刻探索

sunset-3325080_1920

關注我

mark

轉載請務必註明原創地址爲:http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/java

前言

在 Spring Boot 項目的啓動類中常見代碼以下:react

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

其中也就兩個比較引人注意的地方:web

  • @SpringBootApplication
  • SpringApplication.run()

對於第一個註解 @SpringBootApplication,我已經在博客 Spring Boot 2.0系列文章(六):Spring Boot 2.0中SpringBootApplication註解詳解 中詳細的講解了。接下來就是深刻探究第二個了 SpringApplication.run()spring

換個姿式

上面的姿式太簡單了,只一行代碼就完事了。編程

SpringApplication.run(SpringbotApplication.class, args);

實際上是支持作一些個性化的設置,接下來咱們換個姿式瞧瞧:bootstrap

@SpringBootApplication
public class SpringbotApplication {  
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(SpringbotApplication.class);
        // 自定義應用程序的配置
        //app.setXxx()
        app.run(args)
    }
}

沒錯,就是經過一個構造函數,而後設置相關的屬性,從而達到定製化服務。有哪些屬性呢?springboot

SpringApplicationFileds

屬性對應的 get/set 方法app

springapplication_getset

看到沒,還不少呢!less

舉個例子:你想把 Spring Boot 項目的默認 Banner 換成你本身的,就須要在這裏以下:webapp

public static void main(String[] args) {
//      SpringApplication.run(Springboot2Application.class, args);
  SpringApplication application = new SpringApplication(Springboot2Application.class);
  application.setBanner((environment, sourceClass, out) -> {
    //這裏打印一個logo
    System.out.println("      _      _       _\n" +
                       "     | |    (_)     | |\n" +
                       " ____| |__   _  ___ | |__    ___  _ __    __ _\n" +
                       "|_  /| '_ \\ | |/ __|| '_ \\  / _ \\| '_ \\  / _` |\n" +
                       " / / | | | || |\\__ \\| | | ||  __/| | | || (_| |\n" +
                       "/___||_| |_||_||___/|_| |_| \\___||_| |_| \\__, |\n" +
                       "                                          __/ |\n" +
                       "                                         |___/\n");
  });
  application.setBannerMode(Banner.Mode.CONSOLE);
  //你還能夠幹其餘的定製化初始設置
  application.run(args);
}

如今重啓項目,你就會發現,控制檯的 logo 已經換成你本身的了。

banner

固然了,你可能會以爲這樣寫有點複雜,嗯嗯,確實,這樣硬編碼在代碼裏確實不太友好。你還能夠在src/main/resources路徑下新建一個banner.txt文件,banner.txt中填寫好須要打印的字符串內容便可。

從該類中能夠看到在 Spring Boot 2 中引入了個新的 WebApplicationType 和 WebEnvironment。

springapplication-002

webapplicationtype

springapplication-003

確實,這也是 Spring Boot 2 中比較大的特性,它是支持響應式編程的。我以前在文章 Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性詳解 中也介紹過,之後有機會會介紹它的,這裏我先賣個關子。

SpringApplication 初始化

SpringApplication.run()  的實現纔是咱們要深刻探究的主角,該方法代碼以下:

//靜態方法,可用於使用默認配置運行 SpringApplication
public static ConfigurableApplicationContext run(Class<?> primarySource,
      String... args) {
  return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
  return new SpringApplication(primarySources).run(args);
}

在這個靜態方法中,建立 SpringApplication 對象,並調用該對象的 run 方法。

public SpringApplication(Class<?>... primarySources) {
  this(null, primarySources);
}
//建立一個 SpringApplication 實例,應用上下文會根據指定的主要資源加載 beans ,實例在調用 run 方法以前能夠定製化
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  this.webApplicationType = deduceWebApplicationType();
  setInitializers((Collection) getSpringFactoriesInstances(
    ApplicationContextInitializer.class));
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
}

首先是進入單個參數的構造方法,而後進入兩參數的構造方法(ResourceLoader 爲 null),而後進行初始化。

一、deduceWebApplicationType() : 推斷應用的類型 ,建立的是一個 SERVLET 應用仍是 REACTIVE應用或者是 NONE

private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

private WebApplicationType deduceWebApplicationType() {
  if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
      && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
    return WebApplicationType.REACTIVE; //該程序是 REACTIVE 程序
  }
  for (String className : WEB_ENVIRONMENT_CLASSES) {
    if (!ClassUtils.isPresent(className, null)) {
      return WebApplicationType.NONE;   //該程序爲 NONE
    }
  }
  return WebApplicationType.SERVLET;    //默認返回是 SERVLET 程序
}

二、setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)):初始化 classpath 下的全部的可用的 ApplicationContextInitializer。

1)、getSpringFactoriesInstances()

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
  return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//獲取全部的 Spring 工廠實例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  // Use names and ensure unique to protect against duplicates
  Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //獲取全部 Spring Factories 的名字
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                                                     classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances); //Spring 工廠實例排序
  return instances;
}
//根據讀取到的名字建立對象(Spring 工廠實例)
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
 Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
  List<T> instances = new ArrayList<>(names.size());
  for (String name : names) {
    try {
      Class<?> instanceClass = ClassUtils.forName(name, classLoader);
      Assert.isAssignable(type, instanceClass);
      Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
      T instance = (T) BeanUtils.instantiateClass(constructor, args);
      instances.add(instance);
    }
    catch (Throwable ex) {
      throw new IllegalArgumentException(
        "Cannot instantiate " + type + " : " + name, ex);
    }
  }
  return instances;
}

上面的 SpringFactoriesLoader.loadFactoryNames() ,是從 META-INF/spring.factories 的資源文件中,讀取 key 爲org.springframework.context.ApplicationContextInitializer 的 value。

springfactoriesloader

而 spring.factories 的部份內容以下:

2018-05-01_22-21-20

能夠看到,最近的獲得的,是 ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer 這四個類的名字。

2)、setInitializers():

public void setInitializers(
            Collection<? extends ApplicationContextInitializer<?>> initializers) {
  this.initializers = new ArrayList<>();
  this.initializers.addAll(initializers);
}

因此,這裏 setInitializers() 所獲得的成員變量 initializers 就被初始化爲ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer 這四個類的對象組成的 list。

三、setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)):初始化 classpath 下的全部的可用的 ApplicationListener。

1)、getSpringFactoriesInstances() 和上面的相似,可是它是從 META-INF/spring.factories 的資源文件中,獲取到 key 爲 org.springframework.context.ApplicationListener 的 value。

2018-05-01_22-33-56

2)、setListeners():

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
  this.listeners = new ArrayList<>();
  this.listeners.addAll(listeners);
}

因此,這裏 setListeners() 所獲得的成員變量 listeners 就被初始化爲 ClearCachesApplicationListener,ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener ,ConfigFileApplicationListener,DelegatingApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener,LiquibaseServiceLocatorApplicationListener 這九個類的對象組成的 list。

四、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;
}

run 方法背後的祕密

上面看完了構造方法後,已經初始化了一個 SpringApplication 對象,接下來調用其 run 方法,代碼以下:

//運行 Spring 應用程序,建立並刷新一個新的 ApplicationContext
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);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            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;
    }

可變個數參數 args 便是咱們整個應用程序的入口 main 方法的參數。StopWatch 是來自 org.springframework.util 的工具類,能夠用來方便的記錄程序的運行時間。

再來看看 1.5.12 與 2.0.1 版本的 run 方法 有什麼不同的地方?

difference-1.5-2.0

接下來好好分析上面新版本(2.0.1)的 run 方法的代碼並配合比較舊版本(1.5.12)。

一、configureHeadlessProperty():設置 headless 模式

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;

private void configureHeadlessProperty() {
  System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
    SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

其實是就是設置系統屬性 java.awt.headless,該屬性會被設置爲 true。

二、getRunListeners():加載 SpringApplicationRunListener 對象

//TODO:  xxx
SpringApplicationRunListeners listeners = getRunListeners(args);//初始化監聽器
listeners.starting();
try {
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  refreshContext(context);
  afterRefresh(context, applicationArguments);
  listeners.started(context);
  callRunners(context, applicationArguments);
}
try {
  listeners.running(context);
}

private SpringApplicationRunListeners getRunListeners(String[] args) {
  Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
    SpringApplicationRunListener.class, types, this, args));
}

三、new DefaultApplicationArguments(args) :獲取啓動時傳入參數 args(main 方法傳進來的參數) 並初始化爲 ApplicationArguments 對象。

public DefaultApplicationArguments(String[] args) {
  Assert.notNull(args, "Args must not be null");
  this.source = new Source(args);
  this.args = args;
}

四、prepareEnvironment(listeners, applicationArguments):根據 listeners 和 applicationArguments 配置SpringBoot 應用的環境。

private ConfigurableEnvironment prepareEnvironment(
  SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
  // Create and configure the environment
  ConfigurableEnvironment environment = getOrCreateEnvironment();
  configureEnvironment(environment, applicationArguments.getSourceArgs());
  listeners.environmentPrepared(environment);
  bindToSpringApplication(environment);
  if (this.webApplicationType == WebApplicationType.NONE) {
    environment = new EnvironmentConverter(getClassLoader())
      .convertToStandardEnvironmentIfNecessary(environment);
  }
  ConfigurationPropertySources.attach(environment);
  return environment;
}
//若是 environment 不爲空,直接 get 到,不然建立
private ConfigurableEnvironment getOrCreateEnvironment() {
  if (this.environment != null) {
    return this.environment;
  }
  if (this.webApplicationType == WebApplicationType.SERVLET) {
    return new StandardServletEnvironment();
  }
  return new StandardEnvironment();
}
//配置環境
protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
  configurePropertySources(environment, args);//配置要使用的PropertySources
  configureProfiles(environment, args);//配置要使用的Profiles
}
//將環境綁定到 SpringApplication
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
  try {
    Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
  }
  catch (Exception ex) {
    throw new IllegalStateException("Cannot bind to SpringApplication", ex);
  }
}

五、configureIgnoreBeanInfo(environment):根據環境信息配置要忽略的 bean 信息

public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
  if (System.getProperty(
    CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
    Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
                                             Boolean.class, Boolean.TRUE);
    System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,
                       ignore.toString());
  }
}

六、printBanner(environment):打印標誌,上面我已經說過了。

private Banner printBanner(ConfigurableEnvironment environment) {
  if (this.bannerMode == Banner.Mode.OFF) { //若是設置爲 off,不打印 Banner
    return null;
  }
  ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
    : new DefaultResourceLoader(getClassLoader());
  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);
}

七、createApplicationContext():根據應用類型來肯定該 Spring Boot 項目應該建立什麼類型的 ApplicationContext ,默認狀況下,若是沒有明確設置的應用程序上下文或應用程序上下文類,該方法會在返回合適的默認值。

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
  Class<?> contextClass = this.applicationContextClass;
  if (contextClass == null) {
    try {
      switch (this.webApplicationType) {    //根據應用程序的類型來初始化容器
        case SERVLET:   //servlet 應用程序
          contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:  //reactive 應用程序
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:        //默認
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
      }
    } catch (ClassNotFoundException ex) {
  throw new IllegalStateException(
    "Unable create a default ApplicationContext,please specify an           ApplicationContextClass",ex);
    }
  }
  //最後經過Spring的工具類 BeanUtils 初始化容器類 bean
  return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

來看看在 1.5.12 中是怎麼樣的?

createApplicationContext

八、exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context)

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  // Use names and ensure unique to protect against duplicates
  Set<String> names = new LinkedHashSet<>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
       classLoader, args, names);//根據類型 key 爲 SpringBootExceptionReporter 去加載
  AnnotationAwareOrderComparator.sort(instances);//對實例排序
  return instances;
}

這裏也是經過 SpringFactoriesLoader 加載 META-INF/spring.factories 中 key 爲 SpringBootExceptionReporter 的。

springbootexception

九、prepareContext(context, environment, listeners, applicationArguments, printedBanner):完成整個容器的建立與啓動以及 bean 的注入功能。

//裝配 Context
private void prepareContext(ConfigurableApplicationContext context,
   ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
   ApplicationArguments applicationArguments, Banner printedBanner) {
  //將以前準備好的 environment 設置給建立好的 ApplicationContext 使用
  context.setEnvironment(environment);
  //一、
  postProcessApplicationContext(context);
  //二、
  applyInitializers(context);
  listeners.contextPrepared(context);
  if (this.logStartupInfo) {//啓動日誌
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }
  // Add boot specific singleton beans
  context.getBeanFactory().registerSingleton("springApplicationArguments",
                                             applicationArguments);
  if (printedBanner != null) {
    context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
  }
  // 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);
}

1)、postProcessApplicationContext(context)

public static final String CONFIGURATION_BEAN_NAME_GENERATOR = "org.springframework.context.annotation.internalConfigurationBeanNameGenerator";

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
  if (this.beanNameGenerator != null) {
    context.getBeanFactory().registerSingleton(
      AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
      this.beanNameGenerator);
  }
  if (this.resourceLoader != null) {
    if (context instanceof GenericApplicationContext) {
      ((GenericApplicationContext) context)
      .setResourceLoader(this.resourceLoader);
    }
    if (context instanceof DefaultResourceLoader) {
      ((DefaultResourceLoader) context)
      .setClassLoader(this.resourceLoader.getClassLoader());
    }
  }
}

該方法對 context 進行了預設置,設置了 ResourceLoader 和 ClassLoader,並向 bean 工廠中添加了一個beanNameGenerator 。

2)、applyInitializers(context)

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);
  }
}

在刷新以前將任何 ApplicationContextInitializer 應用於上下文

3)、load(context, sources.toArray(new Object[0]))

主要是加載各類 beans 到 ApplicationContext 對象中。

protected void load(ApplicationContext context, Object[] sources) {
  BeanDefinitionLoader loader = createBeanDefinitionLoader( //2
    getBeanDefinitionRegistry(context), sources);// 1
  if (this.beanNameGenerator != null) {
    loader.setBeanNameGenerator(this.beanNameGenerator);
  }
  if (this.resourceLoader != null) {
    loader.setResourceLoader(this.resourceLoader);
  }
  if (this.environment != null) {
    loader.setEnvironment(this.environment);
  }
  loader.load();//3
}

(1)、getBeanDefinitionRegistry(context)

獲取 bean 定義註冊表

private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
  if (context instanceof BeanDefinitionRegistry) {
    return (BeanDefinitionRegistry) context;
  }
  if (context instanceof AbstractApplicationContext) {
    return (BeanDefinitionRegistry) ((AbstractApplicationContext) context)
      .getBeanFactory();
  }
  throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}

(2)、createBeanDefinitionLoader()

經過 BeanDefinitionLoader 的構造方法把參數(註冊表、資源)傳進去,而後建立 BeanDefinitionLoader。

(3)、load()

把資源所有加載。

十、refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
  refresh(context);//1
  if (this.registerShutdownHook) {
    try {
      context.registerShutdownHook();
    }
    catch (AccessControlException ex) {
      // Not allowed in some environments.
    }
  }
}
//刷新底層的 ApplicationContext
protected void refresh(ApplicationContext applicationContext) {
  Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
  ((AbstractApplicationContext) applicationContext).refresh();
}

refreshContext(context) 方法又調用了 refresh(context)。在調用了 refresh(context) 方法以後,調用了 registerShutdownHook 方法。繼續看它的 refresh 方法:

public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    prepareRefresh();
    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);
    try {
      // Allows post-processing of the bean factory in context subclasses.
      postProcessBeanFactory(beanFactory);
      // Invoke factory processors registered as beans in the context.
      invokeBeanFactoryPostProcessors(beanFactory);
      // Register bean processors that intercept bean creation.
      registerBeanPostProcessors(beanFactory);
      // Initialize message source for this context.
      initMessageSource();
      // Initialize event multicaster for this context.
      initApplicationEventMulticaster();
      // Initialize other special beans in specific context subclasses.
      onRefresh();
      // Check for listener beans and register them.
      registerListeners();
      // Instantiate all remaining (non-lazy-init) singletons.
      finishBeanFactoryInitialization(beanFactory); //1
      // Last step: publish corresponding event.
      finishRefresh();
    } catch (BeansException ex) {
      。。。
        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
      // Reset 'active' flag.
      cancelRefresh(ex);
      // Propagate exception to caller.
      throw ex;
    } finally {
      // Reset common introspection caches in Spring's core, since we
      // might not ever need metadata for singleton beans anymore...
      resetCommonCaches();
    }
  }
}

到這裏,咱們就看見重點了,仔細看上的註釋,正在作各類初始化工做,而今天咱們關注的重點就是方法 finishBeanFactoryInitialization(beanFactory)。該方法進行了非懶加載 beans 的初始化工做。如今咱們進入該方法內部,一探究竟。

finishbeanFactoryini

看上圖方法中的最後一步,調用了 beanFactory 的 preInstantiateSingletons() 方法。此處的 beanFactory 是哪一個類的實例對象呢?

2018-05-02_16-17-50

能夠看到 ConfigurableListableBeanFactory 接口的實現類只有 DefaultListableBeanFactory,咱們看下實現類中的 preInstantiateSingletons 方法是怎麼作的。

public void preInstantiateSingletons() throws BeansException {
  // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

  // Trigger initialization of all non-lazy singleton beans...
  for (String beanName : beanNames) {
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      if (isFactoryBean(beanName)) {
        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
        if (bean instanceof FactoryBean) {
          final FactoryBean<?> factory = (FactoryBean<?>) bean;
          boolean isEagerInit;
         if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean){
            isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
             ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext());
          } else {
            isEagerInit = (factory instanceof SmartFactoryBean &&
                           ((SmartFactoryBean<?>) factory).isEagerInit());
          }
          if (isEagerInit) {
            getBean(beanName);
          }
        }
      } else {
        getBean(beanName);
      }
    }
  }

  // Trigger post-initialization callback for all applicable beans...
  for (String beanName : beanNames) {
    Object singletonInstance = getSingleton(beanName);
    if (singletonInstance instanceof SmartInitializingSingleton) {
      final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
      if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
          smartSingleton.afterSingletonsInstantiated();
          return null;
        }, getAccessControlContext());
      } else {
        smartSingleton.afterSingletonsInstantiated();
      }
    }
  }
}

從上面的代碼中能夠看到不少調用了 getBean(beanName) 方法,跟蹤此方法進去後,最終發現 getBean 調用了AbstractBeanFactory 類的 doGetBean(xxx) 方法,doGetBean(xxx) 方法中有這麼一段代碼:

2018-05-02_17-17-39

2018-05-02_17-19-31

可是 createBean() 方法並無獲得實現,實現類在 AbstractAutowireCapableBeanFactory 中。這纔是建立 bean 的核心方法。

createBean

不知不覺,代碼看的愈來愈深,感受思惟都差點回不去 run 方法了,切回大腦的上下文線程到 run 方法去。

十一、afterRefresh(context, applicationArguments):在上下文刷新後調用該方法,其內部沒有作任何操做。

2018-05-02_17-43-23

發現沒作任何操做了以後,就以爲有點奇怪,因此把當前版本和 1.5.12 對比了下,發現:

afterRefresh

在 1.5.12 中的 afterRefresh() 方法中調用了 callRunners() 方法,可是在 2.0.1 版本中的 run 方法中調用了 callRunners () 方法:

2018-05-02_17-57-52

這裏不得不說 SpringApplicationRunListeners 在 2.0.1 中的改變:

2018-05-02_18-28-17

能夠發如今 run 方法中,SpringApplicationRunListeners 監聽器的狀態花生了變化,這也是經過對比不一樣版本的代碼才知道的區別,因此說咱們看源碼須要多對比着看。

so,咱們來看下這個 SpringApplicationRunListeners 這個接口:

2018-05-02_18-33-20

started 狀態:The context has been refreshed and the application has started but CommandLineRunner and ApplicationRunner have not been called

running 狀態:Called immediately before the run method finishes, when the application context has been refreshed and all CommandLineRunner and ApplicationRunners have been called.

相關文章

一、Spring Boot 2.0系列文章(一):Spring Boot 2.0 遷移指南

二、Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性詳解

三、Spring Boot 2.0系列文章(三):Spring Boot 2.0 配置改變

四、Spring Boot 2.0系列文章(四):Spring Boot 2.0 源碼閱讀環境搭建

五、Spring Boot 2.0系列文章(五):Spring Boot 2.0 項目源碼結構預覽

六、Spring Boot 2.0系列文章(六):Spring boot 2.0 中 SpringBootApplication 註解詳解

七、Spring Boot 2.0系列文章(七):SpringApplication 深刻探索

總結

本文從源碼級別分析了 Spring Boot 應用程序的啓動過程,着重看了 SpringApplication 類中的 run 方法其內部實現,並把涉及到的流程代碼都過了一遍。

感悟:有時候跟代碼跟着跟着,發現越陷越深,好難跳出來!後面還需多向別人請教閱讀源碼的技巧!

最後

雖然源碼很難,但隨着不斷的探索,源碼在你面前將會一覽無遺,享受這種探索後的成就感!加油!騷年!

本身本人能力有限,源碼看的很少,上面若有不對的還請留言交流。

相關文章
相關標籤/搜索