介绍
上一章介绍了springBoot启动时候,还未运行run方法时的初始化。现在继续介绍run方法的内容。
下面就是run方法的所有内容了,接下来会分步去介绍各个类和方法的作用。
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, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
StopWatch
stopWatch是一个计数器,通过源码发现,就是在程序运行时候start,在停止时候stop。然后拿到totalTimeMillis就是程序的启动时间。我们也可以自己去使用,他也提供了优雅的打印方法 prettyPrint();
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.startTimeMillis = System.currentTimeMillis();
}
/**
* Stop the current task. The results are undefined if timing
* methods are called without invoking at least one pair
* {@code start()} / {@code stop()} methods.
* @see #start()
*/
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
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("ms % Task name\n");
sb.append("-----------------------------------------\n");
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(5);
nf.setGroupingUsed(false);
NumberFormat pf = NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);
for (TaskInfo task : getTaskInfo()) {
sb.append(nf.format(task.getTimeMillis())).append(" ");
sb.append(pf.format(task.getTimeSeconds() /
getTotalTimeSeconds())).append(" ");
sb.append(task.getTaskName()).append("\n");
}
}
return sb.toString();
}
配置Headless
当程序运行没有外显设备,比如鼠标键盘等信息时候,依然运行程序。
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
SpringApplicationRunListeners
接下来就要介绍到SpringApplicationRunListeners了,我们知道springBoot在启动时候有很多的listener启动,去监听我们发送的事件进行处理。
那springBoot自己的listener是怎么产生监听的呢。主要就是靠这个SpringApplicationRunListeners,它主要是在springBoot启动时候使用,主要是如何作用的呢?直接看代码吧。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
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;
}
getSpringFactoriesInstances 这个方法在上一节的时候,有想知道细节的小伙伴可以去看下上一章。主要作用就是通过SpringFactoriesLoader.loadFactoryNames方法,从META-INF/spring.fatcories中拿到SpringApplicationRunListener的具体的类,然后通过反射进行实例化,这里提一下这边在调用getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)时候, this就是这个springApplication作为参数传递,因为实例化时候构造参数需要,在后面的listener中用到。
通过spring-boot的META-INF/spring.fatcories的文件中拿到具体类信息。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
然后我们去 EventPublishingRunListener 中查看信息。
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();
// 把之前在初始化时候找到的ApplicationListener加到广播中
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
/**
暂时省略其他代码
*/
}
从构造函数中我们就看到了之前传入的springApplication。
这里先创建一个event的广播,然后把之前在执行run方法之前初始化时候实例化的ApplicationListner放到广播中。它的作用就是当有事件过来的时候,会去调用广播方法,然后轮训所有的listener,然后满足条件的(泛型,后面会介绍到),就调用这个listener的onApplicationEvent方法。
这边的处理方式是不是听着很熟悉。对的,他就是观察者模式。
像我们一般写的观察者,都是直接subject里面一个加list属性,提供一个register方法,把观察者注册进去。然后事件直接通过循环list的listener发送出去。
spring内部是通过一个ApplicationEventMulticaster进行了解耦,listener放进了这个广播中。我们通过调用这个多播器进行派发事件。
这样我们在用spring写事件的时候,即使不懂什么是观察者模式也没有关系,写好要发的事件;然后实现ApplicationListener写好listener后(spring4.2之后也可以直接在方法写@EventListener注解也可以,这个后面也会介绍)。通过applicationContext.publishEvent(event)发送出去之后,实现监听的listener直接就能接收到了。这样既不需要我们懂观察者模式,也不需要我们自己注册观察者,都由spring自己做好了。
SpringApplicationRunListeners.starting()
接下来按照SpringApplication的run方法的流程,继续向下看。接下来到SpringApplicationRunListeners的starting;
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
这边看就是调用EventPublishingRunListener的starting方法,然后调用广播发布ApplicationStartingEvent事件。 那spring的listener是如何分辨是自己该执行的事件呢?继续向代码中分析。
事件监听
@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));
for (final ApplicationListener<?> listener : getApplicationListeners(event,
type))
{
// 判断有没有线程池
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
// 执行listner
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent
event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
// listener调用执行事件方法
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName()))
{
// Possibly a lambda-defined listener which we could not resolve the
generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener,
ex);
}
}
else {
throw ex;
}
}
}
通过上述代码发现拿到了listener之后就开始执行listener的对于事件的处理了,那对于listener的筛选在哪里呢?在 getApplicationListeners 这个方法中,那我们就来看看这个方法。
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
// 拿到要发送的消息
Object source = event.getSource();
// 消息的类型
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// 通过缓存查询
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 双重检查
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
// 根据消息的类型和事件的类型判断
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// 不需要缓存
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
再继续向下看retrieveApplicationListeners方法。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable
ListenerRetriever retriever) {
LinkedList<ApplicationListener<?>> allListeners = new LinkedList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
// 在之前通过反射创建广播的时候,已经把SpringApplication的listener加进来了
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>
(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
// 判断是否满足条件
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName,
ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener,
eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition)
// disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
还是没有看到具体是怎么判断listener如何满足的,继续看 supportsEvent(listener, eventType, sourceType) 的方法;
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?>
sourceType) {
// 判断是否是GenericApplicationListener,不然就创建适配器
GenericApplicationListener smartListener = (listener instanceof
GenericApplicationListener ? (GenericApplicationListener) listener : new
GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) &&
smartListener.supportsSourceType(sourceType));
}
再看下创建适配器的方法,这边提一下ResolvableType 这个类,这个类是spring对于class的一个封装,通过这个类,可以拿到class的接口,继承类,以及泛型参数的信息。具体逻辑大家就自己看吧,不展开介绍了,后面应该会单独介绍这个类的。现在只需要知道,这边是根据listener的类型,然后向上找到ApplicationListener这个接口的泛型参数。
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
Assert.notNull(delegate, "Delegate listener must not be null");
this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
@Nullable
static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
ResolvableType resolvableType =
ResolvableType.forClass(listenerType).as(ApplicationListener.class);
return (resolvableType.hasGenerics() ? resolvableType.getGeneric() : null);
}
@Nullable
private static ResolvableType
resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
if (declaredEventType == null || declaredEventType.isAssignableFrom(
ResolvableType.forClass(ApplicationEvent.class))) {
Class<?> targetClass = AopUtils.getTargetClass(listener);
if (targetClass != listener.getClass()) {
declaredEventType = resolveDeclaredEventType(targetClass);
}
}
return declaredEventType;
}
拿到了listener的泛型参数之后,大家应该能想到是根据什么判断了吧?对的,就是根据Listener的泛型参数类型,和事件的类型(eventType)做比较。如果listener的泛型类型是事件类型(eventType)的接口或者超类,那就满足条件。直接看适配器代码逻辑吧。
@Override
public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof SmartApplicationListener) {
Class<? extends ApplicationEvent> eventClass = (Class<? extends
ApplicationEvent>) eventType.resolve();
return (eventClass != null && ((SmartApplicationListener)
this.delegate).supportsEventType(eventClass));
}
else {
return (this.declaredEventType == null ||
// 判断是不是事件的超类或者接口
this.declaredEventType.isAssignableFrom(eventType));
}
}
@Override
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
return !(this.delegate instanceof SmartApplicationListener) ||
((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
}
至此我们终于明白了,为什么spring把所有的listener放到一个广播里面,还能根据不同的事件区分。主要就是根据listener实现ApplicationListener的泛型类型和发送事件的类型相匹配,从而实行区分的。其实上面还有一个根据beanType查找时supportsEvent(listenerType, eventType)方法判断listener,原理和上面一样,就不分析了。
总结
本篇主要是根据springBoot的启动流程来分析SpringApplicationRunListeners的starting方法,这里面主要用到了几个listener的监听事件。从而引申出spring对于listener的使用。现在我们了解了spring的listener主要是用到了观察者模式,并且spring抽象出来一个广播的概念,由广播给我们派发事件,然后由符合要求的listener处理事件。对于如何是符合要求的listener呢,主要是依赖listener实现ApplicationListener的泛型类型和发送事件的类型相匹配,从而实行区分的。
对于SpringApplicationRunListeners的starting方法里面用到的几个事件处理呢,因为并不是很重要的处理,所以暂时没有细看。后面会介绍一些比较重要的事件处理,以及我们会用到的一些事件处理,方式都差不多,后面再做详细介绍吧。如果我再看SpringApplicationRunListeners的starting中有比较特殊重要的事件处理,再来补上。