在学习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的源码对比起来,细节方面还是变化很大的。当然,这些还只是其中的冰山一角,后面我会继续与大家分享在源码阅读中的一些心得体会,
热门工具 换一换