3-1 理解 SpringApplication
3-2 基础技术和衍生技术
3-3 合并工程
新建module
3-4 SpringApplication 准备阶段
SpringApplication 运行
SpringApplication.run(DiveInSpringBootApplication.class, args)
自定义 SpringApplication
- 通过
SpringApplication API
调整
SpringApplication springApplication = new
SpringApplication(DiveInSpringBootApplication.class);
springApplication.setBannerMode(Banner.Mode.CONSOLE);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.setAdditionalProfiles("prod");
springApplication.setHeadless(true);
- 通过
SpringApplicationBuilder API
调整
new SpringApplicationBuilder(DiveInSpringBootApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true)
.run(args);
配置 Spring Boot Bean 源
Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为 Spring Bean 定义。
数量:一个或多个以上。
Java 配置 Class
用于 Spring 注解驱动中 Java 配置类,大多数情况是 Spring 模式注解所标注的类,如
@Configuration
。
XML 上下文配置文件
用于
Spring
传统配置驱动中的 XML 文件。
推断 Web 应用类型
根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:
Web Reactive: WebApplicationType.REACTIVE
Web Servlet: WebApplicationType.SERVLET
非 Web: WebApplicationType.NONE
参考方法:
org.springframework.boot.SpringApplication#deduceWebApplicationType
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
3-7 推断引导类
根据 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;
}
3-8 加载应用上下文初始器
加载应用上下文初始器 (
ApplicationContextInitializer
)
利用
Spring
工厂加载机制,实例化ApplicationContextInitializer
实现类,并排序对象集合。
- 实现
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 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.core.io.support.SpringFactoriesLoader
- 配置资源:
META-INF/spring.factories
- 排序:
AnnotationAwareOrderComparator#sort
实践
spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.test3_8.AfterHelloApplicationContextInitializer,\
com.example.test3_8.HelloApplicationContextInitializer
Application3_8.java
@SpringBootApplication
public class Application3_8 {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}
}
AfterHelloApplicationContextInitializer.java
@Order
public class AfterHelloApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("AfterHelloApplicationContextInitializer\t" + configurableApplicationContext.getId());
}
}
HelloApplicationContextInitializer.java
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("HelloApplicationContextInitializer\t" + configurableApplicationContext.getId());
}
}
输出结果
3-9 加载应用事件监听器
加载应用事件监听器(
ApplicationListener
)利用
Spring
工厂加载机制,实例化ApplicationListener
实现类,并排序对象集合
spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.test3_8.AfterHelloApplicationContextInitializer,\
com.example.test3_8.HelloApplicationContextInitializer
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.test2_9.HelloWorldAutoConfiguration2_9
# Application Listeners
org.springframework.context.ApplicationListener=\
com.example.test3_9.HelloApplicationListener
HelloApplicationListener.java
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println("HelloApplicationListener\t" + contextRefreshedEvent.getApplicationContext().getId()
+ "\ttime\t" + contextRefreshedEvent.getTimestamp());
}
}
3-10 SpringApplication 运行阶段
加载 SpringApplication
运行监听器( SpringApplicationRunListeners
)
利用
Spring
工厂加载机制,读取SpringApplicationRunListener
对象集合,并且封装到组合类SpringApplicationRunListeners
。
运行 SpringApplication
运行监听器( SpringApplicationRunListeners
)
SpringApplicationRunListener
监听多个运行状态方法
监听方法 | 阶段说明 | Spring Boot 起始版本 |
starting() | Spring 应用刚启动 | 1.0 |
environmentPrepared(ConfigurableEnvironment) | ConfigurableEnvironment 准备妥当,允许将其调整 | 1.0 |
contextPrepared(ConfigurableApplicationContext) | ConfigurableApplicationContext 准备妥当,允许将其调整 | 1.0 |
contextLoaded(ConfigurableApplicationContext) | ConfigurableApplicationContext 已装载,但仍未启动 | 1.0 |
started(ConfigurableApplicationContext) | ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成 | 2.0 |
running(ConfigurableApplicationContext) | Spring 应用正在运行 | 2.0 |
failed(ConfigurableApplicationContext,Throwable) | Spring 应用运行失败 | 2.0 |
3-11 SpringApplication 运行监听器事件监听器编程模型
监听 Spring Boot 事件 / Spring 事件
Spring Boot
通过SpringApplicationRunListener
的实现类EventPublishingRunListener
利用Spring Framework
事件API
,广播Spring Boot
事件
Spring Framework 事件/监听器编程模型
-
Spring 应用事件
- 普通应用事件: ApplicationEvent
- 应用上下文事件: ApplicationContextEvent
-
Spring 应用监听器
- 接口编程模型: ApplicationListener
- 注解编程模型: @EventListener
-
Spring 应用事广播器
- 接口: ApplicationEventMulticaster
- 实现类: SimpleApplicationEventMulticaster
- 执行模式:同步或异步
HelloBootstrapApplication3_11.java
public class HelloBootstrapApplication3_11 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.addApplicationListener( e->{
System.out.println("监听到事件:\t" + e.getClass());
});
//启动以用上下文
context.refresh();
context.publishEvent("hey man!");
context.publishEvent(new ApplicationEvent("hi,my girl"){});
//关闭应用上下文
context.close();
}
}
注意:
如果出现以下错误:Exception in thread "main" java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@2a33fae0, started on Thu Jan 01 08:00:00 CST 1970 at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:424) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:373) at com.example.test3_11.HelloBootstrapApplication3_11.main(HelloBootstrapApplication3_11.java:13)
问题分析:
应用尚未启动,不能发布事件
解决办法:
先启动应用再发布事件。
EventPublishingRunListener
监听方法与 Spring Boot
事件对应关系
监听方法 | Spring Boot 事件 | Spring Boot 起始版本 |
---|---|---|
starting() | ApplicationStartingEvent | 1.5 |
environmentPrepared(ConfigurableEnvironment) | ApplicationEnvironmentPreparedEvent | 1.0 |
contextPrepared(ConfigurableApplicationContext) | contextLoaded(ConfigurableApplicationContext) ApplicationPreparedEvent | 1.0 |
started(ConfigurableApplicationContext) | ApplicationStartedEvent | 2.0 |
running(ConfigurableApplicationContext) | ApplicationReadyEvent | 2.0 |
failed(ConfigurableApplicationContext,Throwable) | ApplicationFailedEvent | 1.0 |
3-13 监听 Spring Boot 事件
application.properties
name=whaleson
ConfigFileApplicationListener3_13.java
public class ConfigFileApplicationListener3_13 implements Ordered, SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> event) {
return true;
}
@Override
public int getOrder() {
return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
ApplicationEnvironmentPreparedEvent config = (ApplicationEnvironmentPreparedEvent)event;
String name = config.getEnvironment().getProperty("name");
System.out.println("Your name is:\t" + name);
}
if (event instanceof ApplicationPreparedEvent) {
}
}
}
spring.factories
org.springframework.context.ApplicationListener=\
com.example.test3_13.ConfigFileApplicationListener3_13
说明
ConfigFileApplicationListener
是用于加载application.properties
和application.yml
文件。当自定义Listener读取配置文件的信息时,优先级不超过ConfigFileApplicationListener.DEFAULT_ORDER
。
3-14 创建 Spring 应用上下文
创建 Spring
应用上下文( ConfigurableApplicationContext
)
根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableApplicationContext 实例:
Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
Web Servlet: AnnotationConfigServletWebServerApplicationContext
非 Web: AnnotationConfigApplicationContext
创建 Environment
根据准备阶段的推断 Web 应用类型创建对应的
ConfigurableEnvironment
实例:
Web Reactive: StandardEnvironment
Web Servlet: StandardServletEnvironment
非 Web: StandardEnvironment
SpringApplication.java
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
说明
: 根据web类型创建不同类型的applicationContext。
Application3_14.java
非web
public class Application3_14 {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(Application3_14.class)
.web(WebApplicationType.NONE)
.run(args);
System.out.println("ConfigurableApplicationContext application type:\t" + context.getClass());
System.out.println("Environment type:\t" + context.getEnvironment().getClass().getName());
}
}
输出:
ConfigurableApplicationContext application type: class org.springframework.context.annotation.AnnotationConfigApplicationContext
Environment type: org.springframework.core.env.StandardEnvironment