本文從SpringApplication類開始分析Spring Boot應(yīng)用的啟動過程磕仅,使用的Spring Boot版本是1.5.15.RELEASE贝咙。
成員變量
public class SpringApplication {
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
/**
* Default banner location.
*/
public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
/**
* Banner location property key.
*/
public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private static final Log logger = LogFactory.getLog(SpringApplication.class);
private final Set<Object> sources = new LinkedHashSet<Object>();
private Class<?> mainApplicationClass;
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
private boolean logStartupInfo = true;
private boolean addCommandLineProperties = true;
private Banner banner;
private ResourceLoader resourceLoader;
private BeanNameGenerator beanNameGenerator;
private ConfigurableEnvironment environment;
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
private boolean webEnvironment;
private boolean headless = true;
private boolean registerShutdownHook = true;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
private Map<String, Object> defaultProperties;
private Set<String> additionalProfiles = new HashSet<String>();
}
- DEFAULT_CONTEXT_CLASS常量表示非Web環(huán)境默認(rèn)的應(yīng)用上下文是AnnotationConfigApplicationContext類型睬隶;
- DEFAULT_WEB_CONTEXT_CLASS常量表示W(wǎng)eb環(huán)境下默認(rèn)的應(yīng)用上下文是AnnotationConfigEmbeddedWebApplicationContext類型峭梳;
- sources表示bean來源箱叁;
- mainApplicationClass表示啟動類類型肖揣;
- resourceLoader表示資源加載的策略;
- beanNameGenerator表示生成bean名稱的策略可免;
- environment表示使用的環(huán)境抓于;
- applicationContextClass表示創(chuàng)建的應(yīng)用上下文的類型;
- webEnvironment表示當(dāng)前是否是Web環(huán)境浇借;
- initializers表示添加的ApplicationContextInitializer捉撮;
- listeners表示添加的ApplicationListener;
- ...
構(gòu)造函數(shù)
SpringApplication類的兩個構(gòu)造函數(shù)如下妇垢,它們都調(diào)用initialize方法做初始化工作巾遭。
public SpringApplication(Object... sources) {
initialize(sources);
}
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
this.resourceLoader = resourceLoader;
initialize(sources);
}
initialize方法代碼如下:
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
該方法做了如下工作:
- deduceWebEnvironment方法檢測當(dāng)前是否是Web環(huán)境,只有當(dāng)Servlet接口和ConfigurableWebApplicationContext接口都存在且能被加載時才是Web環(huán)境闯估;
private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
- getSpringFactoriesInstances方法從jar包的META/spring.factories文件中獲取特定類型的工廠實現(xiàn)類限定名并實例化它們灼舍,此處是創(chuàng)建了ApplicationContextInitializer和ApplicationListener兩種類型的工廠實現(xiàn)類,并分別保存到initializers和listeners成員變量中涨薪。每個jar包都可能有spring.factories文件骑素,以spring-boot-1.5.15.RELEASE.jar中的META/spring.factories為例,該文件包含4種ApplicationContextInitializer和9種ApplicationListener尤辱;
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener
- deduceMainApplicationClass方法找出main方法所在的類并保存到mainApplicationClass成員變量砂豌。
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靜態(tài)方法代碼如下所示,可見其調(diào)用了上文的構(gòu)造函數(shù)和run成員方法光督。
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
run成員方法代碼如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
run成員方法的主要流程如下:
- 創(chuàng)建SpringApplicationRunListener類型的實例阳距;
- 為應(yīng)用上下文準(zhǔn)備環(huán)境;
- 創(chuàng)建應(yīng)用上下文结借;
- 準(zhǔn)備應(yīng)用上下文筐摘;
- 刷新應(yīng)用上下文。
創(chuàng)建SpringApplicationRunListener
在創(chuàng)建SpringApplicationRunListener的過程中船老,首先getRunListeners方法在各META/spring.factories文件查找org.springframework.boot.SpringApplicationRunListener的工廠實現(xiàn)類咖熟,然后以該SpringApplication對象和main方法的命令行參數(shù)為實參調(diào)用這些實現(xiàn)類的構(gòu)造函數(shù)實例化它們,最后各SpringApplicationRunListener的starting回調(diào)方法被調(diào)用柳畔。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
-
SpringApplicationRunListener接口專門用于監(jiān)聽run方法馍管,其代碼如下所示,該接口的實現(xiàn)類需要聲明一個公有的以SpringApplication和字符串?dāng)?shù)組為參數(shù)的構(gòu)造函數(shù)薪韩。
public interface SpringApplicationRunListener { void starting(); void environmentPrepared(ConfigurableEnvironment environment); void contextPrepared(ConfigurableApplicationContext context); void contextLoaded(ConfigurableApplicationContext context); void finished(ConfigurableApplicationContext context, Throwable exception); }
- SpringApplicationRunListeners類是SpringApplicationRunListener的集合确沸,其方法內(nèi)部都是依次調(diào)用各SpringApplicationRunListener同名的回調(diào)方法。
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<SpringApplicationRunListener>(listeners); } public void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } public void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } } // 省略一些代碼 }
- 以spring-boot-1.5.15.RELEASE.jar中的META/spring.factories為例俘陷,該文件只包含一個EventPublishingRunListener罗捎;
EventPublishingRunListener類的部分代碼如下,它的構(gòu)造函數(shù)將SpringApplication的listeners成員變量保存的各ApplicationListener加入事件廣播器中拉盾,其他回調(diào)方法在內(nèi)部廣播了相應(yīng)的事件給各ApplicationListener桨菜。# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override @SuppressWarnings("deprecation") public void starting() { this.initialMulticaster .multicastEvent(new ApplicationStartedEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { } // 省略一些代碼
準(zhǔn)備環(huán)境
準(zhǔn)備環(huán)境是由prepareEnvironment方法完成的,其代碼如下所示捉偏。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
- 首先創(chuàng)建環(huán)境倒得,若是Web環(huán)境則創(chuàng)建StandardServletEnvironment,否則創(chuàng)建StandardEnvironment夭禽;
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webEnvironment) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
- 然后是配置環(huán)境屎暇,分別配置了屬性源和配置文件;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); } protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } } protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); // ensure they are initialized // But these ones should go first (last wins in a property key clash) Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(profiles.toArray(new String[profiles.size()])); }
- 最后各SpringApplicationRunListener的environmentPrepared回調(diào)方法被調(diào)用驻粟。
創(chuàng)建應(yīng)用上下文
createApplicationContext方法創(chuàng)建上下文根悼,如果applicationContextClass字段指定了上下文類型則創(chuàng)建該類型的應(yīng)用上下文,否則對Web環(huán)境默認(rèn)創(chuàng)建AnnotationConfigEmbeddedWebApplicationContext蜀撑,對非Web環(huán)境創(chuàng)建AnnotationConfigApplicationContext挤巡。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
所創(chuàng)建的應(yīng)用上下文即是DispatcherServlet的應(yīng)用上下文,這與傳統(tǒng)的Spring MVC工程不同酷麦。
準(zhǔn)備應(yīng)用上下文
prepareContext方法用于準(zhǔn)備上面創(chuàng)建的應(yīng)用上下文矿卑,其代碼如下所示。
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 = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
該方法主要做了以下幾件事:
- 為創(chuàng)建的應(yīng)用上下文設(shè)置了之前創(chuàng)建的環(huán)境沃饶;
- postProcessApplicationContext方法做了創(chuàng)建應(yīng)用上下文后的處理工作母廷,為應(yīng)用上下文注冊了bean名稱生成策略轻黑,子類可以重寫該方法自定義其他工作;
- applyInitializers方法按各ApplicationContextInitializer的添加順序調(diào)用其initialize回調(diào)方法琴昆,請注意除了上文提到j(luò)ar包的META/spring.factories文件中可以指定該接口的工廠實現(xiàn)類之外氓鄙,還可以在調(diào)用run方法之前通過SpringApplication類的addInitializers成員方法添加ApplicationContextInitializer;
- 各SpringApplicationRunListener的contextPrepared回調(diào)方法被調(diào)用业舍;
- load方法將bean定義加載到應(yīng)用上下文中(還未實例化)抖拦;
- 各SpringApplicationRunListener的contextLoaded回調(diào)方法被調(diào)用。
刷新應(yīng)用上下文
refreshContext方法刷新創(chuàng)建的應(yīng)用上下文舷暮,根據(jù)上一步加載的bean定義實例化各單例bean态罪。
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
應(yīng)用上下文刷新后
應(yīng)用上下文刷新后run方法調(diào)用了afterRefresh方法執(zhí)行所有的Runner,并調(diào)用各SpringApplicationRunListener的finished回調(diào)方法下面。
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
afterRefresh方法代碼如下所示:
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
- 首先找出應(yīng)用上下文中所有的ApplicationRunner和CommandLineRunner复颈,實際使用時這兩個接口的實現(xiàn)類用@Component注解修飾即可;
- 然后對這些ApplicationRunner和CommandLineRunner按升序排序沥割,它們的實現(xiàn)類都可以選擇地實現(xiàn)Ordered接口或者用@Order注解修飾券膀;
- 最后按順序調(diào)用這些Runner的run回調(diào)方法。