定义自己的starter
1.认识SpringApplication
SpringApplication
类提供了一种可通过运行 main()
方法来启动 Spring 应用的简单方式。多数情况下,您只需要委托给静态的 SpringApplication.run
方法:
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}
如果 SpringApplication
的默认设置不符合您的想法,您可以创建本地实例进行定制化。例如,要关闭 banner,您可以这样:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringConfiguration.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
2.SpringApplication.run执行流程
SpringApplication可用于从 Java 主方法引导和启动 Spring 应用程序的类。默认情况下,类将执行以下步骤来启动应用:
创建一个适当的[
ApplicationContext
]实例(取决于您的类路径)注册 [
CommandLinePropertySource
]以将命令行参数公开为 Spring 属性刷新应用程序上下文,加载所有单例 bean
触发任何[
CommandLineRunner
]bean
下面我们就来详细分析一下它的执行流程,见:org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
//创建秒表,用来计算启动事件
StopWatch stopWatch = new StopWatch();
//启动秒表
stopWatch.start();
//Spring IOC 容器对象
ConfigurableApplicationContext context = null;
//收集Spring Boot 异常报告器的list
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置无头属性,java.awt.headless
configureHeadlessProperty();
//SpringBoot的SpringApplication run方法的侦听器 监听器,
//SpringApplicationRunListeners维护了一个 SpringApplicationRunListener 集合
SpringApplicationRunListeners listeners = getRunListeners(args);
//会触发所有 SpringApplicationRunListener#starting的执行
//,会通过SimpleApplicationEventMulticaster广播一个ApplicationStartingEvent事件
listeners.starting();
try {
//把应用参数封装到DefaultApplicationArguments,通过它可以访问应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建环境对象,Environment包括了property和profile
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置忽略 Bean 信息 ,spring.beaninfo.ignore
configureIgnoreBeanInfo(environment);
//打印横幅
Banner printedBanner = printBanner(environment);
//创建IOC容器对象 AnnotationConfigApplicationContext
context = createApplicationContext();
//创建Spring Boot 异常报告器实例。会扫描spring.factories下的 FailureAnalyzers实例,
//FailureAnalyzer是用于分析故障并提供可显示给用户的诊断信息
//比如:NoSuchBeanDefinitionFailureAnalyzer ; DataSourceBeanCreationFailureAnalyzer
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] {
ConfigurableApplicationContext.class }, context);
//刷新容器准备工作
//1.把environment绑定到context容器对象
//2.context后置处理,比如绑定resourceLoader
//3.触发 ApplicationContextInitializer#initialize初始化(用于在刷新之前初始化Context回调接口。)
//4.触发 listener.contextPrepared ,抛出 ApplicationContextInitializedEvent 事件
//5.把ApplicationArguments注册到容器中成为一个Bean
//6.把 Banner注册到容器中成为一个Bean
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器,底层走spring的刷新容器流程
refreshContext(context);
//空方法,留给我们扩展
afterRefresh(context, applicationArguments);
//暂定秒表
stopWatch.stop();
if (this.logStartupInfo) {
//打印秒表记录的时间
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//触发 SpringApplicationRunListener#started方法抛出 ApplicationStartedEvent 事件
listeners.started(context);
//调用 ApplicationRunner 和 CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//处理异常,会从exceptionReporters拿出异常进行打印
//以及会触发 SpringApplicationRunListeners#failed,广播 ApplicationFailedEvent事件
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//执行listeners.running , 抛出 ApplicationReadyEvent 事件
listeners.running(context);
}
catch (Throwable ex) {
//处理异常,会从exceptionReporters拿出异常进行打印
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
画一个流程图总结一下
3.StopWatch秒表
Spring体用的秒表,允许对多个任务进行计时,显示每个命名任务的总运行时间和运行时间。隐藏System.nanoTime()的使用,提高应用程序代码的可读性并减少计算错误的可能性。注意,此对象并非设计为线程安全的,也不使用同步。
public class StopWatch {
/**
* Identifier of this {@code StopWatch}.
* <p>Handy when we have output from multiple stop watches and need to
* distinguish between them in log or console output.
*/
//任务的ID
private final String id;
private boolean keepTaskList = true;
//任务列表
private final List<TaskInfo> taskList = new LinkedList<>();
/** Start time of the current task. */
//开始时间
private long startTimeNanos;
/** Name of the current task. */
//当前任务名
@Nullable
private String currentTaskName;
@Nullable
private TaskInfo lastTaskInfo;
//任务数量
private int taskCount;
/** Total running time. */
//总时间
private long totalTimeNanos;
//开始任务,穿了一个“”作为taskName
public void start() throws IllegalStateException {
start("");
}
/**
* Start a named task.
* <p>The results are undefined if {@link #stop()} or timing methods are
* called without invoking this method first.
* @param taskName the name of the task to start
* @see #start()
* @see #stop()
*/
//开始任务
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
//任务名
this.currentTaskName = taskName;
//记录开始时间
this.startTimeNanos = System.nanoTime();
}
//停止秒表
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
//时间差
long lastTime = System.nanoTime() - this.startTimeNanos;
//累计时间
this.totalTimeNanos += lastTime;
//创建一个TaskInfo任务信息
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
//加入任务列表
this.taskList.add(this.lastTaskInfo);
}
//增加任务数量
++this.taskCount;
//清空任务名
this.currentTaskName = null;
}
//以优雅的格式打印秒表记录的时间日志
public String prettyPrint() {
StringBuilder sb = new StringBuilder(shortSummary());
sb.append('\n');
if (!this.keepTaskList) {
sb.append("No task info kept");
}
else {
sb.append("---------------------------------------------\n");
sb.append("ns % Task name\n");
sb.append("---------------------------------------------\n");
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(9);
nf.setGroupingUsed(false);
NumberFormat pf = NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);
for (TaskInfo task : getTaskInfo()) {
sb.append(nf.format(task.getTimeNanos())).append(" ");
sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" ");
sb.append(task.getTaskName()).append("\n");
}
}
return sb.toString();
}
}
StopWatch秒表可以用来对多个任务计时,,start的时候会使用System.nanoTime()来获时间记录到startTimeNanos ,stop结束方计算时间差,然后会把每次的时间和任务名封装成TaskInfo,加入taskList。最后会累计每次任务的时间总额。提供了prettyPrint方法以优雅的格式组织秒表记录的时间日志。
但是要注意:虽然它可以允许多个任务记时,但是它并不是线程安全的。
4.SpringBootExceptionReporter异常报告
4.1.核心类认识
SpringBootExceptionReporter是用于支持自定义上报SpringApplication启动错误的回调接口,它可以把启动的错误日志汇报给用户
@FunctionalInterface
public interface SpringBootExceptionReporter {
/**
* Report a startup failure to the user.
* @param failure the source failure
* @return {@code true} if the failure was reported or {@code false} if default
* reporting should occur.
*/
boolean reportException(Throwable failure);
}
reportException方法的作用就是为用户报告错误。它的唯一实现类是 FailureAnalyzers ,它提供了
final class FailureAnalyzers implements SpringBootExceptionReporter {
private static final Log logger = LogFactory.getLog(FailureAnalyzers.class);
private final ClassLoader classLoader;
//故障分析仪
private final List<FailureAnalyzer> analyzers;
//报告指定的异常
@Override
public boolean reportException(Throwable failure) {
//把异常封装到FailureAnalysis
//FailureAnalysis中维护了很多的FailureAnalyzer,它的作用是分析故障并提供可显示给用户的诊断信息
FailureAnalysis analysis = analyze(failure, this.analyzers);
return report(analysis, this.classLoader);
}
//分析异常
private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
for (FailureAnalyzer analyzer : analyzers) {
try {
//把Throwable异常信息封装成FailureAnalysis
FailureAnalysis analysis = analyzer.analyze(failure);
if (analysis != null) {
return analysis;
}
}
catch (Throwable ex) {
logger.debug(LogMessage.format("FailureAnalyzer %s failed", analyzer), ex);
}
}
return null;
}
private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
//加载FailureAnalysisReporter, FailureAnalysisReporter用来 向用户报告FailureAnalysis分析。
List<FailureAnalysisReporter> reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class,
classLoader);
if (analysis == null || reporters.isEmpty()) {
return false;
}
for (FailureAnalysisReporter reporter : reporters) {
//报告异常
reporter.report(analysis);
}
return true;
}
reportException方法接收一个Throwable ,然后Throwable 会被封装到FailureAnalysis。然后通过SpringFactoriesLoader去加载FailureAnalysisReporter(向用户报告FailureAnalysis分析),通过FailureAnalysisReporter去报告异常。FailureAnalysis结构如下
public class FailureAnalysis {
//异常描述
private final String description;
private final String action;
//异常对象
private final Throwable cause;
LoggingFailureAnalysisReporter结构如下见:org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter#report
public final class LoggingFailureAnalysisReporter implements FailureAnalysisReporter {
private static final Log logger = LogFactory.getLog(LoggingFailureAnalysisReporter.class);
@Override
public void report(FailureAnalysis failureAnalysis) {
if (logger.isDebugEnabled()) {
logger.debug("Application failed to start due to an exception", failureAnalysis.getCause());
}
//把错误日志打印到控制台
if (logger.isErrorEnabled()) {
logger.error(buildMessage(failureAnalysis));
}
}
//构建错误日志内容
private String buildMessage(FailureAnalysis failureAnalysis) {
StringBuilder builder = new StringBuilder();
builder.append(String.format("%n%n"));
builder.append(String.format("***************************%n"));
builder.append(String.format("APPLICATION FAILED TO START%n"));
builder.append(String.format("***************************%n%n"));
builder.append(String.format("Description:%n%n"));
builder.append(String.format("%s%n", failureAnalysis.getDescription()));
if (StringUtils.hasText(failureAnalysis.getAction())) {
builder.append(String.format("%nAction:%n%n"));
builder.append(String.format("%s%n", failureAnalysis.getAction()));
}
return builder.toString();
}
4.2.报告异常
在SpringApplication#run方法中有try-catch操作,如果启动出现异常,会执行org.springframework.boot.SpringApplication#handleRunFailure来处理异常
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
//处理退出码,发布一个ExitCodeEvent事件
handleExitCode(context, exception);
if (listeners != null) {
//发布ApplicationFailedEvent事件
listeners.failed(context, exception);
}
}
finally {
//报告异常,通过LoggingFailureAnalysisReporter 把异常打印到控制台
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
上面重要是发布ApplicationFailedEvent事件, 然后通过SpringBootExceptionReporter#reportException去把异常打印到控制台,
5.监听器机制
上面代码中有很多地方都出现了事件发布,比如: SpringApplicationRunListeners listeners = getRunListeners(args)
它的作用是广播ApplicationStartingEvent事件,这用到了Spring的监听器机制。我们可以认为以 Listenner 结尾的类都是监听器,监听器使用到了观察者设计模式,其作用是监听一些事件的发生从而进行一些操作。监听器的好处是可以实现代码解耦,对此你可能不是很能理解,我这里用一个js例子来代理理解事件机制
function dothing(){
//回调函数
}
//监听button的click事件
$("#button").click(dothing);
上面代码相信你是写过的,就是一个JS监听按钮点击事件,这里需要明确三个角色
- button : 事件源,这个事件发生在谁身上
- click : 事件类型 ,按钮发生了什么事件
- dothing : 回调函数,当button被点击,触发 dothing函数。
那么Java中的事件机制和上面案例很相似,我这里有个案例:当用户注册成功,给用户推送一条短信,使用事件机制来实现
这么理解这幅图
- 首先需要定义一个事件类型RegisterApplicationEvent 继承于ApplicationEvent , 代表的注册这个事件,好比是"click"
- 然后需要在注册逻辑中,使用事件发布器ApplicationEventPublisher 发布该事件 ,好比 button 被 click了
- 事件被发布,需要触发某段逻辑,所以要写一个监听器类实现ApplicationListernner,该监听器监听的是“注册事件”。
- 然后就调用短信发送逻辑发送短信即可。好比是上面的dothing回调函数。
相信大致的流程你是看懂了,但是有些陌生类让我们比较迷惑,下面我们就来系统的认识一下这些类。
5.1. 核心类认识
EventListener
EventListener是java提供的最顶层的监听器接口,不管是Servlet的监听器还是Spring的监听器都是该接口的子类(所有事件侦听器接口都必须实现于接口)。
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
ApplicationListener
Spring提供的基于 Observer 设计模式(观察者)设计的监听器接口,实现了EventListener。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
编写Spring的监听器都需要实现 ApplicationListener这个接口 , 比如这个类
public class ScheduledAnnotationBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent>...省略{
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.applicationContext) {
// Running in an ApplicationContext -> register tasks this late...
// giving other ContextRefreshedEvent listeners a chance to perform
// their work at the same time (e.g. Spring Batch's job registration).
finishRegistration();
}
}
}
ScheduledAnnotationBeanPostProcessor就Schedule定时任务的处理器 ,它是实现ApplicationListener监听器接口,监听了ContextRefreshedEvent容器刷新完成事件,也就是说当Spring容器刷新完成,就会触发 onApplicationEvent方法,那么该监听器就可以对 Scheduled做一些处理了。
ApplicationEvent
Spring提供的事件对象,实现于EventObject,它代表了事件的类型。下面是EventObject的结构
public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @exception IllegalArgumentException if source is null.
*/
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
/**
* The object on which the Event initially occurred.
*
* @return The object on which the Event initially occurred.
*/
public Object getSource() {
return source;
}
/**
* Returns a String representation of this EventObject.
*
* @return A a String representation of this EventObject.
*/
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}
Object source; 可以用来传送数据 ,当我们要发布一个事件的时候,就需要创建一个事件对象,同时事件对象指定一个shource。在Spring中内置了很多事件对象,他们都是ApplicationEvent的子类比如:
- ContextStartedEvent : ApplicationContext Spring容器启动时引发的事件。
- ContextRefreshedEvent : 初始化或刷新ApplicationContext时引发的事件。
- ContextStoppedEvent:当ApplicationContext停止时引发的事件。
- ContextClosedEvent:ApplicationContext关闭时引发的事件。
下面这几个是SpringBoot提供的事件对象,也是ApplicationEvent的子类,在上面的SpringBoot执行流程中也有看到
- ApplicationPreparedEvent : 当SpringApplication启动并且ApplicationContext已完全准备好但未刷新时发布的事件。 将加载 bean 定义,并且在此阶段Environment已准备好使用
- ApplicationStartingEvent : 在SpringApplication启动后尽可能早地发布事件 - 在Environment或ApplicationContext可用之前,但在ApplicationListener已注册之后
- ApplicationStartedEvent :刷新应用程序上下文但在调用任何application和command line运行程序之前发布的事件。
- ApplicationReadyEvent :发布该事件表明应用程序已准备好为请求提供服务
- ApplicationFailedEvent : SpringApplication启动失败时发布的事件
ApplicationEventPublisher
事件发布器,提供了发布事件的基础功能
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
大名鼎鼎的 ApplicationContext 容器对象就是实现该接口ApplicationEventPublisher ,拥有了事件发布的能力。
他们的继承体系如下
5.2.发布自定义事件
我们来把上面的案例实现以下,即:当注册成功,给用户发送一条短信,确定好三个角色
- 事件是什么?“注册成功事件” -> 需要定义一个事件对象
- 事件源是什么?“注册逻辑” -> 在注册成功后,发布"注册事件"
- 做出的反映是什么?“推送短信” -> 监听“注册事件”,推送短信
第一步,创建一个事件对象,继承ApplicationEvent
//注册成功事件对象
public class RegisterSuccessEvent extends ApplicationEvent {
//Object source :待会要传递的参数
public RegisterSuccessEvent(Object source) {
super(source);
}
}
第二步,创建事件监听器,监听RegisterSuccessEvent事件
//注册成功的监听器,监听 RegisterSuccessEvent事件
@Slf4j
@Service
public class RegisterSuccessListenner implements ApplicationListener<RegisterSuccessEvent> {
@Override
public void onApplicationEvent(RegisterSuccessEvent event) {
Map<String,String> source = (Map<String, String>) event.getSource();
if(source == null){
log.error("短信内容不能为空");
}else{
String phone = source.get("phone");
String message = source.get("message");
log.info("注册成功,给: {} 发送短信,内容: {}" , phone , message);
}
}
}
第三步,注册逻辑,抛出RegisterSuccessEvent事件,我这里就简单模拟一下
//注入事件发布器
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@RequestMapping("/register")
public JSONResult register(){
Map<String,String> map = new HashMap<>();
map.put("phone","18233333333");
map.put("message","注册成功");
applicationEventPublisher.publishEvent(new RegisterSuccessEvent(map));
return JSONResult.builder().build();
}
第四步,启动测试,效果如下
2022-01-14 10:10:31.941 INFO 20448 --- [nio-8081-exec-5] cn.whale.event.RegisterSuccessListenner : 注册成功,给: 18233333333 发送短信,内容: 注册成功
这里我再给一个小案例:项目启动完成,往数据库初始化一个管理员账号,这个应该怎么做呢?
我们可以编写一个类,实现 ApplicationListener<ContextRefreshedEvent>
,监听事件是Spring容器刷新完成,然后复写onApplicationEvent
方法,在该方法中往数据库保存管理员账号即可。你可以自己试着写一下。
5.3.web三大监听器
这里给大家介绍web环境中,三个比较有用的监听器
HttpSessionListener : 它是针对于Session的监听器,提供了两个方法来监听session的创建和销毁,代码如下
public interface HttpSessionListener extends EventListener {
//session创建
public default void sessionCreated(HttpSessionEvent se) {
}
//session销毁
public default void sessionDestroyed(HttpSessionEvent se) {
}
}
如果我们实现该接口,就可以监听到session的什么周期方法了,你可能会问,这有什么用,那我给一个需求你思考一下怎么做:“记录用户在网站的登录到退出的时间”
ServletContextListener : 它是用来监听ServletContext上下文对象的生命周期,结构如下
public interface ServletContextListener extends EventListener {
//上下文初始化,代表应用启动
public default void contextInitialized(ServletContextEvent sce) {
}
//上下文销毁,代表应用结束
public default void contextDestroyed(ServletContextEvent sce) {
}
}
我这么说把,Spirng整合web,就是通过实现ServletContextListenner监听器 ,复写 contextInitialized方法,然后在该方法中创建WebApplicationContext容器。所以该监听器可以监听整个程序的启动和销毁。
ServletRequestListener : 它是监听request对象的生命周期方法
public interface ServletRequestListener extends EventListener {
//销毁
public default void requestDestroyed (ServletRequestEvent sre) {
}
//request初始化
public default void requestInitialized (ServletRequestEvent sre) {
}
}
该监听器是监听request对象的初始化和销毁 ,我不知道你有没有通过 RequestContextHolder.getRequestAttributes() 来获取Request对象,它的实现原理就是通过一个 RequestContextListener类去实现 ServletRequestListener 接口,然后在requestInitialized方法中拿到request对象,封装到RequestContextHolder中的一个 ThreadLocal<RequestAttributes> requestAttributesHolder
中存储起来,所以我们在业务代码中可以通过RequestContextHolder直接获取Request对象。
6.SpringBoot的事件
讲完Spirng事件我们来讲SpringBoot中的事件,在SpringBoot启动流程中出现 SpringApplicationRunListeners#starting 等代码,其实就是在发布SpringBoot的事件。我们来看
4.1.SpringApplicationRunListeners
SpringApplicationRunListeners : 下面是 SpringApplicationRunListeners的代码结构
class SpringApplicationRunListeners {
private final Log log;
//保存了很多的SpringApplicationRunListener
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
//调用starting,本质是调用SpringApplicationRunListener的starting
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
//调用environmentPrepared环境准备完成,本质是调用SpringApplicationRunListener的environmentPrepared
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
//调用contextPrepared 容器刷新完成,本质是调用SpringApplicationRunListener的contextPrepared
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
//容器加载完成
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
//启动
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
//运行中
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
//出现错误
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
//出现错误
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
try {
listener.failed(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
}
在 SpringApplicationRunListeners这类中维护了一个 List<SpringApplicationRunListener> listeners;
集合,SpringApplicationRunListeners#starting方法本质是调用的SpringApplicationRunListener#starting
方法
4.2.SpringApplicationRunListener
SpringApplicationRunListener是一个接口,其中维护了很多的事件方法
public interface SpringApplicationRunListener {
//开始
default void starting() {
}
//环境准备完成
default void environmentPrepared(ConfigurableEnvironment environment) {
}
//容器准备完成
default void contextPrepared(ConfigurableApplicationContext context) {
}
//容器加载完成
default void contextLoaded(ConfigurableApplicationContext context) {
}
//启动成功
default void started(ConfigurableApplicationContext context) {
}
//运行中
default void running(ConfigurableApplicationContext context) {
}
//启动失败
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
这里我找到了它的唯一一个默认实现类EventPublishingRunListener , 看名字是一个时间发布器
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
...省略...
//简单应用事件广播器
private final SimpleApplicationEventMulticaster initialMulticaster;
@Override
public void starting() {
//通过 SimpleApplicationEventMulticaster 发布ApplicationStartingEvent 应用开始启动事件
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
//通过 SimpleApplicationEventMulticaster 发布ApplicationEnvironmentPreparedEvent 应用环境准备完成事件
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
//通过 SimpleApplicationEventMulticaster 发布ApplicationContextInitializedEvent 应用初始化完成事件
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);
}
//通过 SimpleApplicationEventMulticaster 发布ApplicationPreparedEvent 应用准备成功事件
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
//通过 ConfigurableApplicationContext 发布ApplicationStartedEvent 应用启动成功事件
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
//通过 ConfigurableApplicationContext 发布ApplicationReadyEvent 应用启动完毕,可以接受请求了
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());
//通过 initialMulticaster 发布ApplicationFailedEvent 应用启动失败
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);
//打印错误
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}
在EventPublishingRunListener中维护了一个 SimpleApplicationEventMulticaster 事件广播器,其中 starting ;environmentPrepared ;contextPrepared;contextLoaded ;failed 使用的是SpringBoot提供的SimpleApplicationEventMulticaster去发布事件。而对于;started ;failed; running ;会用到 ConfigurableApplicationContext容器对象去发布事件,ConfigurableApplicationContext是ApplicationEventPublisher Spring的事件发布器的实现类。
下面这几个是SpringBoot提供的事件对象,也是ApplicationEvent的子类,在上面的SpringBoot执行流程中也有看到
- ApplicationPreparedEvent : 当SpringApplication启动并且ApplicationContext已完全准备好但未刷新时发布的事件。 将加载 bean 定义,并且在此阶段Environment已准备好使用
- ApplicationStartingEvent : 在SpringApplication启动后尽可能早地发布事件 - 在Environment或ApplicationContext可用之前,但在ApplicationListener已注册之后
- ApplicationStartedEvent :刷新应用程序上下文但在调用任何application和command line运行程序之前发布的事件。
- ApplicationReadyEvent :发布该事件表明应用程序已准备好为请求提供服务
- ApplicationFailedEvent : SpringApplication启动失败时发布的事件
- ApplicationEnvironmentPreparedEvent:当SpringApplication启动并且Environment首次可供检查和修改时发布的事件。
- ApplicationContextInitializedEvent :当SpringApplication启动并准备ApplicationContext并且 ApplicationContextInitializers 已被调用但在加载任何 bean 定义之前发布的事件
我们来看一下 initialMulticaster 是怎么广播事件的,如: this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
,见:org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
...省略...
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//这里是拿到监听了该event事件的Listenner,循环去调用监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//调用监听器
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//错误处理器
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
//调用监听器
doInvokeListener(listener, event);
}
catch (Throwable err) {
//错误处理
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//触发了 ApplicationListener#onApplicationEvent
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
...省略...
}
}
}
你应该看明白了 ,SimpleApplicationEventMulticaster 中先是根据某个事件类型比如 ApplicationStartingEvent ,获取到监听了该事件的ApplicationListener 监听类,循环去调用监听类即:调用ApplicationListener#onApplicationEvent方法去触发事件。
那么如果你的Listenner监听了ApplicationStartingEvent 该事件,你的onApplicationEvent方法就会被触发执行。
那我们再看一下ConfigurableApplicationContext#publishEvent是怎么发布事件的,如:context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
,见:org.springframework.context.support.AbstractApplicationContext#publishEvent
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//这里还是通过 Multicaster 去广播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
我们看到 ConfigurableApplicationContext 其实掉用了AbstractApplicationContext#publishEvent方法去广播事件,底层还是使用了 org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent去广播事件
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
SpringBoot的事件就讲到这,那么我们学会了什么呢?至少你应该知道如果我要在SpirngBoot启动,或者启动失败做一些自己的业务,那你应该怎么做了。
7.printBanner打印横幅
横幅是SpringBoot启动时候在控制台打印的SpringBoot图案
我们可以在resources中创建banner.txt来自定义横幅内容 , 也可以通过 spring.main.banner-mode=off来关闭打印。
7.1.相关类介绍
在SpringBoot中提供了一个Banner接口专门用于以编程方式编写横幅的接口类。
@FunctionalInterface
public interface Banner {
//打印横幅
void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);
//模式
enum Mode {
OFF,
CONSOLE,
LOG
}
}
我们来介绍一下这几个实现类
SpringBootBanner : SpringBoot默认的横幅打印实现类
class SpringBootBanner implements Banner {
//默认要打印的横幅
private static final String[] BANNER = {
"", " . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
//打印横幅
for (String line : BANNER) {
printStream.println(line);
}
//拿到SpringBoot的版本号
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
//打印版本号
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
ImageBanner : 用来打印图片横幅,我们只需要在resources下创建一个 banner.gif ,就可以被打印,也可以使用spring.banner.image.location
来指定横幅的位置。
spring:
main:
banner-mode: console #横幅模式,在控制台打印 off是关闭
banner:
image:
location: classpath:banner.gif #图片横幅,默认也是banner.gif
#height:
#width:
ImageBanner结构如下
public class ImageBanner implements Banner {
private static final String PROPERTY_PREFIX = "spring.banner.image.";
private static final Log logger = LogFactory.getLog(ImageBanner.class);
private static final double[] RGB_WEIGHT = {
0.2126d, 0.7152d, 0.0722d };
private final Resource image;
//打印横幅
private void printBanner(Environment environment, PrintStream out) throws IOException {
//处理图片属性
int width = getProperty(environment, "width", Integer.class, 76);
int height = getProperty(environment, "height", Integer.class, 0);
int margin = getProperty(environment, "margin", Integer.class, 2);
boolean invert = getProperty(environment, "invert", Boolean.class, false);
BitDepth bitDepth = getBitDepthProperty(environment);
PixelMode pixelMode = getPixelModeProperty(environment);
Frame[] frames = readFrames(width, height);
for (int i = 0; i < frames.length; i++) {
if (i > 0) {
resetCursor(frames[i - 1].getImage(), out);
}
//打印
printBanner(frames[i].getImage(), margin, invert, bitDepth, pixelMode, out);
sleep(frames[i].getDelayTime());
}
}
}
7.2.横幅打印流程
横幅的打印是在SpringBoot启动流程中,调用 org.springframework.boot.SpringApplication#printBanner方法实现
private Banner printBanner(ConfigurableEnvironment environment) {
//判断横幅功能是否被关闭,对应yaml中的 spring.main.banner-mode=off
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
//资源加载器,比如:用来加载banner.txt
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
//创建 Banner打印器
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
//如果是log模式走这,对应:spring.main.banner-mode=log
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
//默认是console模式,走这
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
代码中会判断 this.bannerMode == Banner.Mode.OFF
时候开启日志打印(对应yaml中的配置),默认是Console,然后会创建一个ResourceLoader,主要是用来加载banner.txt或banner.gif 资源文件的。然后会判断如果this.bannerMode == Mode.LOG
就使用logger打印,否则使用System.out打印,默认是System.out。打印横幅调用的是SpringApplicationBannerPrinter#print方法完成的。代码来到org.springframework.boot.SpringApplicationBannerPrinter#print
class SpringApplicationBannerPrinter {
//banner.txt的地址
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
//banner.gif的地址
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
//banner文件的名字
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
//图片bannre的支持的后缀
static final String[] IMAGE_EXTENSION = {
"gif", "jpg", "png" };
//默认的banner,使用SpringBootBanner
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
private final ResourceLoader resourceLoader;
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
//获取Banner
Banner banner = getBanner(environment);
//打印Banner
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
//尝试获取image Banner,默认banner.gif,加入Banners
banners.addIfNotNull(getImageBanner(environment));
//尝试获取banner.txt ,加入Banners
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
//返回多个banners
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
//如果没有指定banner.txt或者banner.gif,就会使用默认的SpringBootBanner
return DEFAULT_BANNER;
}
private Banner getTextBanner(Environment environment) {
//以:获取banner的location:spring.banner.location
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
//加载 banner.txt
Resource resource = this.resourceLoader.getResource(location);
if (resource.exists()) {
//返回banner
return new ResourceBanner(resource);
}
//如果不存在banner.txt,那么就会使用默认的SpringBootBanner去打印
return null;
}
private Banner getImageBanner(Environment environment) {
//获取图片banner的地址:spring.banner.image.location
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
if (StringUtils.hasLength(location)) {
//加载图片,banner.gif
Resource resource = this.resourceLoader.getResource(location);
return resource.exists() ? new ImageBanner(resource) : null;
}
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
return null;
}
SpringApplicationBannerPrinter会尝试获取spring.banner.image.location
, 默认指向的resources/banner.gif 以及 spring.banner.location
指向的resources/banner.txt ,然后送resourceLoader加载资源文件,封装成 ImageBanner 或者 ResourceBanner。并交给Banners,Banners维护了Banner的列表。也就是说可以同时打印多个横幅,我们可以通过修改这2个路径来指定banner资源的位置。
如果没有banner.txt 和 banner.gif ,就会使用SpringBootBanner打印默认的banner.
紧接着就会调用Banners#printBanner方法循环执行每个Banner#printBanner方法。具体的Banner是如何打印出来的上面ImageBanner和SpringBootBanner已有介绍。
8.容器创建
8.1.容器介绍
SpringBoot底层是Spring,启动的过程中肯定要创建Spring容器,下面简单介绍一下IOC的容器工厂类
- BeanFactory: Spring的容器顶层工厂,提供了获取Bean的基础方法,所有的IOC容器都是其子类
- XmlWebApplicationContext : 整合web的容器对象,默认从'/WEB-INF/applicationContext.xml'加载配置
- ClassPathXmlApplicationContext : 使用ResourcePatternResolver,默认从classpath加载xml配置的容器对象
- AnnotationConfigApplicationContext :针对于注解的容器对象,SpringBoot使用它
8.2.容器的创建
创建容器是在SpringBoot启动流程中的 org.springframework.boot.SpringApplication#createApplicationContext方法
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:
//默认走这,使用的是AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
//反射创建AnnotationConfigApplicationContext的实例
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
9.容器刷新
9.1.容器刷新
在SpringBoot启动流程中 有一句代码 SpringApplication#prepareContext
, 这句代码在准备Spring容器,代码如下org.springframework.boot.SpringApplication#prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//把environment绑定到容器
context.setEnvironment(environment);
//容器的后置处理,里面也没干啥
postProcessApplicationContext(context);
//触发ApplicationContextInitializer#initialize的调用
applyInitializers(context);
//触发监听器,发布ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//把applicationArguments注册Bean我们可以通过注入 ApplicationArguments 来获取程序启动参数
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//把Bnaner注册为Bean
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
//如果出现相同的Bean,是否允许覆盖,默认为false,
//可用在yaml通过spring.main.allow-bean-definition-overriding: true来指定
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
//延迟初始化
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
//配置类资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//load启动类,把启动类注册为Bean
load(context, sources.toArray(new Object[0]));
//抛出ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
9.2.容器刷新
容器刷新在 org.springframework.boot.SpringApplication#refreshContext 方法中
private void refreshContext(ConfigurableApplicationContext context) {
//刷新容器
refresh(context);
if (this.registerShutdownHook) {
try {
//注册一个钩子,当容器被关闭,
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
上面代码做了两个事情,一个是调用refresh方法刷新容器,二个是向 JVM 的Runtime 注册一个名为SpringContextShutdownHook的关闭钩子,在 JVM 关闭时关闭此上下文,委托容器的 doClose()执行实际的关闭过程。下面是context.registerShutdownHook();的内部代码,见:org.springframework.context.support.AbstractApplicationContext#registerShutdownHook
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
//Runtime: 每个 Java 应用程序都有一个Runtime类的实例,它允许应用程序与运行应用程序的环境进行交互
//给Runtime 注册SpringContextShutdownHook 钩子
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
//发布 ContextClosedEvent 容器关闭事件
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
if (this.lifecycleProcessor != null) {
try {
//生命周期处理器,close触发
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
}
// Destroy all cached singletons in the context's BeanFactory.
//销毁所有的单利Bean
destroyBeans();
//close工厂
// Close the state of this context itself.
closeBeanFactory();
//空实现,让子类做一些最后的清理...
// Let subclasses do some final clean-up if they wish...
onClose();
// Reset local application listeners to pre-refresh state.
if (this.earlyApplicationListeners != null) {
//监听器清理
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Switch to inactive.
//下文当前是否处于活动状态的标志修改为false
this.active.set(false);
}
}
总结一下上面代码:就是给JVM的Runtime注册一个SpringContextShutdownHook钩子,当程序关闭,会触发AbstractApplicationContext#doClose方法,该方法中最了一些销毁和扫尾工作。
接下来就是刷新容器方法 refresh 分析了,代码最终来到 AbstractApplicationContext#refresh 方法中。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1.准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory
//从子容器的refreshBeanFactory方法中载入Bean的资源文件
//2.加载配置,加载Bean,注册Bean就在这儿
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//3.准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,
//BeanPostProcessor和 Aware类的自动装配等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//4.BeanFactory初始化完成的后置工作,
//这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//5.调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6.注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//7.初始化信息源,国际化相关
initMessageSource();
// Initialize event multicaster for this context.
//8.初始化容器事件传播器,比如:SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9.空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
onRefresh();
// Check for listener beans and register them.
//10.注册事件监听器,注册实现了ApplicationListener接口的监听器bean,
//这些监听器是注册到ApplicationEventMulticaster中的
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//11.实例化所有剩余的(非延迟初始化)单例的Bean
//也就是创建Bean的真正实例就在该方法中
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//12.完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
//销毁已经创建的单例Bean。
destroyBeans();
// Reset 'active' flag.
//取消容器刷新
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//重置缓存
resetCommonCaches();
}
}
}
这里其实已经在调用Spirng的容器刷新流程了,这里大概说明一下,具体的详细流程见
- 在obtainFreshBeanFactory方法中回去解析配置文件,然后把Bean封装成BeanDefinition,注册到IOC容器中(一个CurrentHashMap)
- 在invokeBeanFactoryPostProcessors方法中会触发BeanFactory的后置处理器的调用,这个很有用,可以在BeanFactory初始化过程中做扩展
- registerBeanPostProcessors 方法中注册了BeanPostProcessors后置处理器,这个很有用,可以在Bean实例化前后,做一些扩展业务,比如:@Transactionl ,@Autowired的实现原理就是基于Bean的后置处理器
- finishBeanFactoryInitialization :这个方法中会对单利的Bean做正在的实例化,然后缓存到Spring的IOC容器中,以后获取Bean的时候会从缓存中取。这保证了Bean的单利。
10.callRunners
10.1.认识ApplicationRunner 和 CommandLineRunner
SpringBoot启动完成之后的最后一步就是org.springframework.boot.SpringApplication#callRunners,该方法中回去执行所有的ApplicationRunner , 和 CommandLineRunner 。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
ApplicationRunner 和 CommandLineRunner 可以看做是SpringBoot的扩展,两个接口的功能是一样的,通过实现该接口可以在SpringBoot启动成功之后做一些初始化工作。
这两个Runner的不同之处在于CommandLineRunner#run方法接收的是main方法传递过来的直接参数args,而ApplicationRunner#run方法接收的是封装了args的ApplicationArguments对象。我们来演示其中一个
10.2.使用ApplicationRunner
第一步:定义一个Runner类
@Component
@Slf4j
public class ApplicationRunnerBean implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("ApplicationRunnerBean.run SpringBoot启动完成,做一些初始化...");
}
}
第二步:启动测试
文章结束啦,如果文章对你有所帮助,请一定给个好评哦,请一定给个好评哦,请一定给个好评哦