第3章 理解 SpringApplication
Spring Boot2.0深度实践之核心技术篇
– 第3章 理解 SpringApplication
SpringApplication 简介
参考 https://docs.spring.io/spring-boot/docs/2.1.8.RELEASE/reference/html/boot-features-spring-application.html 可以看到;
The SpringApplication
class provides a convenient way to bootstrap a Spring application that is started from a main() method. In many situations, you can delegate to the static SpringApplication.run method
基础技术
- Spring 模式注解
- Spring 应用上下文
- Spring 工厂加载机制
- Spring 应用上下文初始化器
- Spring Environment 抽象
- Spring 应用事件/监听器
衍生技术
- SpringApplication
- SpringApplication Builder API
- SpringApplication 运行监听器
- SpringApplication 参数
- SpringApplication 故障分析
- SpringBoot 应用事件/监听器
SpringApplication 准备阶段
1. 使用方式
- 基础使用
SpringApplication.run(MicroVideoApplication.class, args)
- SpringApplication API
SpringApplication springApplication = new SpringApplication(MicroVideoApplication.class);
springApplication.setBannerMode(Banner.Mode.CONSOLE);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.setAdditionalProfiles("prod");
springApplication.setHeadless(true);
springApplication.run(args);
- SpringApplicationBuilder API
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(MicroVideoApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true)
.run(args);
- sources
Set<String> sources = new HashSet<>();
sources.add(MicroVideoApplication.class.getName());
SpringApplication springApplication = new SpringApplication();
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.setSources(sources);
springApplication.run(args);
2. 配置 Spring Bean 来源
Java 配置 Class
或 XML 上下文配置文件集合
,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为 Spring Bean 定义;
- Java 配置 Class
用于 Spring 注解驱动中 Java 配置类,大多数情况是 Spring 模式注解所标注的类,如 @Configuration 。 - XML 上下文配置文件
用于 Spring 传统配置驱动中的 XML 文件。
3. 推断 Web 应用类型 和 主引导类
根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:
- Web Reactive:
WebApplicationType.REACTIVE
- Web Servlet:
WebApplicationType.SERVLET
- 非 Web:
WebApplicationType.NONE
参考方法: org.springframework.boot.SpringApplication#deduceWebApplicationType
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
根据 Main 线程执行堆栈判断实际的引导类
参考方法: org.springframework.boot.SpringApplication#deduceMainApplicationClass
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;
}
4. 加载 应用上下文初始化器 和 应用事件监听器
- 加载应用上下文初始化器
利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合。
- 实现
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
org/springframework/boot/spring-boot-autoconfigure/2.1.6.RELEASE/spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
-
技术
- 实现类: org.springframework.core.io.support.SpringFactoriesLoader
- 配置资源: META-INF/spring.factories
- 排序: AnnotationAwareOrderComparator#sort
-
实践
package com.example.applicationdemo.context;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class HelloWorldInitializer<C extends ConfigurableApplicationContext> implements ApplicationContextInitializer<C> {
@Override
public void initialize(C applicationContext) {
applicationContext.setId(System.currentTimeMillis() + "");
System.out.println(applicationContext);
}
}
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.applicationdemo.auto.HelloWorldAutoConfiguration
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.applicationdemo.context.HelloWorldInitializer
- 加载应用事件监听器
利用 Spring 工厂加载机制,实例化 ApplicationListener 实现类,并排序对象集合
// ...
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();
// 1. 加载应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 2. 加载应用事件监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {
// ...
}
SpringApplication 运行阶段
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. 加载 SpringApplication 运行监听器(SpringApplicationRunListeners)
利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类 SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
org/springframework/boot/spring-boot/2.1.6.RELEASE/spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
2. 运行 SpringApplication 运行监听器
3. 监听 SpringBoot 事件、Spring 事件
Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener
利用 Spring Framework 事件 API ,广播 Spring Boot 事件。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
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
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(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) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}
EventPublishingRunListener
监听方法与 Spring Boot 事件对应关系
Spring Framework 事件/监听器编程模型
- Spring 应用事件
- 普通应用事件: ApplicationEvent
- 应用上下文事件: ApplicationContextEvent
- Spring 应用监听器
- 接口编程模型: ApplicationListener
- 注解编程模型: @EventListener
- Spring 应用事广播器
- 接口: ApplicationEventMulticaster
- 实现类: SimpleApplicationEventMulticaster
- 执行模式:同步或异步
Spring Framework 监听事件
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationBootStrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.addApplicationListener(event -> {
System.out.println("监听到事件 ----> " + event);
});
applicationContext.refresh();
applicationContext.publishEvent("Hello Event");
applicationContext.publishEvent(new ApplicationEvent("Hello ApplicationEvent") {
});
applicationContext.close();
}
}
运行结果:
监听到事件 ----> org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@299a06ac, started on Tue Sep 17 16:51:49 CST 2019]
监听到事件 ----> org.springframework.context.PayloadApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@299a06ac, started on Tue Sep 17 16:51:49 CST 2019]
监听到事件 ----> com.example.applicationdemo.AnnotationBootStrap$1[source=Hello ApplicationEvent]
监听到事件 ----> org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@299a06ac, started on Tue Sep 17 16:51:49 CST 2019]
自定义 SpringApplicationRunListener
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("HelloSpringApplicationRunListener starting");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("HelloSpringApplicationRunListener environmentPrepared");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener contextPrepared");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener contextLoaded");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener started");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener running");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("HelloSpringApplicationRunListener failed");
}
}
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.example.applicationdemo.listener.HelloSpringApplicationRunListener
测试
public static void main(String[] args) {
new SpringApplicationBuilder(MicroVideoApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true)
.run(args);
}
运行结果:
HelloSpringApplicationRunListener starting
HelloSpringApplicationRunListener environmentPrepared
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)
org.springframework.context.annotation.AnnotationConfigApplicationContext@4c40b76e, started on Thu Jan 01 08:00:00 CST 1970
HelloSpringApplicationRunListener contextPrepared
2019-09-17 17:06:07.365 INFO 16443 --- [ main] c.e.a.MicroVideoApplication : Starting MicroVideoApplication on kevindeMacBook-Pro.local with PID 16443 (/Users/kevin/Documents/idea-project/application-demo/target/classes started by kevin in /Users/kevin/Documents/idea-project/application-demo)
2019-09-17 17:06:07.369 INFO 16443 --- [ main] c.e.a.MicroVideoApplication : The following profiles are active: prod
HelloSpringApplicationRunListener contextLoaded
2019-09-17 17:06:08.508 INFO 16443 --- [ main] c.e.a.MicroVideoApplication : Started MicroVideoApplication in 1.444 seconds (JVM running for 2.115)
HelloSpringApplicationRunListener started
HelloSpringApplicationRunListener running
Process finished with exit code 0
监听 SpringBoot 事件
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
public class MyApplicationListener implements SmartApplicationListener, Ordered {
@Override
public int getOrder() { // 值越小 优先级越高
return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
|| ApplicationPreparedEvent.class.isAssignableFrom(eventType);
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return true;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
ApplicationEnvironmentPreparedEvent preparedEvent = (ApplicationEnvironmentPreparedEvent) event;
Environment environment = preparedEvent.getEnvironment();
System.out.println("environment.getProperty(\"name\") : " + environment.getProperty("name"));
}
if (event instanceof ApplicationPreparedEvent) {
}
}
}
# ApplicationListener 实现配置
org.springframework.context.ApplicationListener=\
com.example.applicationdemo.listener.MyApplicationListener
4. 创建 应用上下文 和 Environment
- 创建 应用上下文(ConfigurableApplicationContext)
根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableApplicationContext 实例:
- Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
- Web Servlet: AnnotationConfigServletWebServerApplicationContext
- 非 Web: AnnotationConfigApplicationContext
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建 Environment
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);
}
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_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);
}
- 创建 Environment
根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableEnvironment 实例:
- Web Reactive: StandardReactiveWebEnvironment
- Web Servlet: StandardServletEnvironment
- 非 Web: StandardEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
测试
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext =
new SpringApplicationBuilder(MicroVideoApplication.class)
.web(WebApplicationType.NONE)
.run(args);
System.out.println(applicationContext.getClass().getName());
System.out.println(applicationContext.getEnvironment().getClass().getName());
applicationContext.close();
}
5. 失败故障分析报告
6. 回调 CommandLineRunner、ApplicationRunner
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MicroVideoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MicroVideoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println("启动成功");
}
}