关于java:springboot源码解析管中窥豹系列之总体结构一

43次阅读

共计 5091 个字符,预计需要花费 13 分钟才能阅读完成。

一、简介

  • 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 封装起来提供更多的性能。

这期就这样,从下期开始,咱们带着问题去源码外面找实现。

欢送关注公众号:丰极,更多技术学习分享。

正文完
 0