在学习 spring boot 2.0 源码之前, 我们先利用 spring initializr 快速地创建一个基本的简单的示例:
1. 先从创建示例中的 main 函数开始读起:
- package com.example;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- /**
- * spring boot 2.0 源码分析
- * author lizongshen
- * date 2018/06/02
- */
- @SpringBootApplication
- public class DemoApplication {public static void main(String[] args) {
- SpringApplication.run(DemoApplication.class, args);
- }
- }
2. 在这里我们可以看到, spring boot 是通过 SpringApplication.run 这个函数来进行启动的, 其中 args 可以传递启动时需要的个性化参数. 跳转到源码中继续一探究竟:
- /**
- * Static helper that can be used to run a {@link SpringApplication} from the
- * specified source using default settings.
- * @param primarySource the primary source to load
- * @param args the application arguments (usually passed from a Java main method)
- * @return the running {@link ApplicationContext}
- */
- public static ConfigurableApplicationContext run(Class<?> primarySource,
- String... args) {
- return run(new Class<?>[] { primarySource }, args);
- }
3. 在这里我们看到, 其把 primarySource 这个参数包装成数组, 跳转到了另外一个同样的方法中.
小发现: SpringApplication.run() 函数是允许同时启动多个 Application 的.
接着往下读
- /**
- * Static helper that can be used to run a {@link SpringApplication} from the
- * specified sources using default settings and user supplied arguments.
- * @param primarySources the primary sources to load
- * @param args the application arguments (usually passed from a Java main method)
- * @return the running {@link ApplicationContext}
- */
- public static ConfigurableApplicationContext run(Class<?>[] primarySources,
- String[] args) {
- return new SpringApplication(primarySources).run(args);
- }
4. 在这段代码中, 我们可以看到 spring boot 把我们所使用的静态方法, 创建出了一个 SpringApplication 的实例, 并启动了实例中的 run 方法.
小知识: 根据这个发现, 我们也可以在 main 函数中, 自己创建 SpringApplication 的实例, 然后调用实例方法 run.
我们来看一下 SpringApplication 的构造函数期间都干了些什么事:
- /**
- * Create a new {@link SpringApplication} instance. The application context will load
- * beans from the specified primary sources (see {@link SpringApplication class-level}
- * documentation for details. The instance can be customized before calling
- * {@link #run(String...)}.
- * @param primarySources the primary bean sources
- * @see #run(Class, String[])
- * @see #SpringApplication(ResourceLoader, Class...)
- * @see #setSources(Set)
- */
- public SpringApplication(Class<?>... primarySources) {
- this(null, primarySources);
- }
- /**
- * Create a new {@link SpringApplication} instance. The application context will load
- * beans from the specified primary sources (see {@link SpringApplication class-level}
- * documentation for details. The instance can be customized before calling
- * {@link #run(String...)}.
- * @param resourceLoader the resource loader to use
- * @param primarySources the primary bean sources
- * @see #run(Class, String[])
- * @see #setSources(Set)
- */
- @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();
- }
在这里主要是初始化了 SpringApplication 的私有属性, 在构造的过程中, 调用了另外一个构造函数, 并传递了 ResourceLoader 的参数.
小知识: 通过 ResourceLoader 这个参数, 我们看到如果自定义 SpringApplication 在初始化过程中, 是可以通过 ResourceLoader 来引入自定义资源的.
接着往下看, 来看看 run 函数的真面目
- /**
- * Run the Spring application, creating and refreshing a new
- * {@link ApplicationContext}.
- * @param args the application arguments (usually passed from a Java main method)
- * @return a running {@link 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;
- }
这段代码很长, 我们先来一点一点地分析.
1. 通过 configureHeadlessProperty(); 这行代码, 配置属性:
- private void configureHeadlessProperty() {
- System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
- SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
- }
2. 通过 SpringApplicationRunListeners listeners = getRunListeners(args); 这行代码获取了监听器:
- private SpringApplicationRunListeners getRunListeners(String[] args) {
- Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
- return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
- SpringApplicationRunListener.class, types, this, args));
- }
3. 通过 listeners.starting(); 这行代码启动监听器:
- public void starting() {
- Iterator var1 = this.listeners.iterator();
- while(var1.hasNext()) {
- SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
- listener.starting();
- }
- }
4. 通过 context = this.createApplicationContext(); 创建了容器
- /**
- * Strategy method used to create the {@link ApplicationContext}. By default this
- * method will respect any explicitly set application context or application context
- * class before falling back to a suitable default.
- * @return the application context (not yet refreshed)
- * @see #setApplicationContextClass(Class)
- */
- protected ConfigurableApplicationContext createApplicationContext() {
- Class<?> contextClass = this.applicationContextClass;
- if (contextClass == null) {
- try {
- switch (this.webApplicationType) {
- case SERVLET:
- contextClass = Class.forName(DEFAULT_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) {
- throw new IllegalStateException(
- "Unable create a default ApplicationContext,"
- + "please specify an ApplicationContextClass",
- ex);
- }
- }
- return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
- }
5. 通过 prepareContext(context, environment, listeners, applicationArguments,printedBanner); 这行代码来准备容器:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
- 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);
- }
6. 通过 this.refreshContext(context); 刷新容器:
- private void refreshContext(ConfigurableApplicationContext context) {
- this.refresh(context);
- if(this.registerShutdownHook) {
- try {
- context.registerShutdownHook();
- } catch (AccessControlException var3) {
- ;
- }
- }
- }
在刷新容器完成之后, 调用 afterRefresh 函数, 跟踪过去发现是空的, 可能是预留了一个扩展点.
通过阅读发现 spring boot 2.0 的源码和 1.x 的源码对比起来, 细节方面还是变化很大的. 当然, 这些还只是其中的冰山一角, 后面我会继续与大家分享在源码阅读中的一些心得体会,
来源: https://www.cnblogs.com/lizongshen/p/9127999.html