springboot源碼解析-管中窺豹系列之總體結構(一)

一、簡介

  • Springboot源碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
  • 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot源碼管中窺豹系列。

 簡介

二、框架

我們先把springboot源碼的框架了解清楚。

1、新建一個springboot項目

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

@SpringBootApplication
public class MyprojectApplication {

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

}

2、分析源碼

相比於spring項目或者springmvc項目,springboot的入口很好找,就在main裡面的run方法,我們進入run方法

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的靜態方法,新建了一個SpringApplication類,調用它的run方法,我們先看SpringApplication的構造方法,再看run方法

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


@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 = WebApplicationType.deduceFromClasspath();
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
}

構造方法主要做了這幾件事:

  • 確定web類型:webApplicationType
  • 載入ApplicationContextInitializer
  • 載入ApplicationListener
  • 確定applicationcontext的實現類

實現細節我們先不探討,接著看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);
    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;
}

run方法做了幾件事:

  • stopwatch記錄springboot啟動耗時
  • 不同階段觸發spring的listen事件
  • 新建applicationcontext實現類
  • 啟動spring:refreshContext(context)
  • 啟動完成載入runner

最重要的就是refreshContext方法,延用的springframe的fresh方法

private void refreshContext(ConfigurableApplicationContext context) {
  refresh((ApplicationContext) context);
  if (this.registerShutdownHook) {
    try {
        context.registerShutdownHook();
    }
    catch (AccessControlException ex) {
        // Not allowed in some environments.
    }
  }
}

AbstractApplicationContext類的方法

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

        // Last step: publish corresponding event.
        finishRefresh();
    }

    catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + 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();
    }
    }
}

先不必深究,看看就好,了解整體的框架,至此springboot項目的源碼主框架就是這樣了。

  • 核心能力還是spring的refresh方法
  • 通過SpringApplication封裝起來提供更多的功能。

這期就這樣,從下期開始,我們帶著問題去源碼裡面找實現。

歡迎關注公眾號:豐極,更多技術學習分享。