前言
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源加载器,null
this.resourceLoader = resourceLoader;
// 主类,一般是启动类
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 环境监测。判断启动的是 mvc 还是 webflux
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置系统初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 配置 main 函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
上面还是我们熟悉的系统初始化
阶段的代码啦,但是呢,我们已经翻阅过系统初始化器的源码啦。
这一小节,我们继续往下看,那就是轮到 监听器 环节咯。
监听器模式
这是前置知识,需要了解一下设计模式之一,观察者模式。
监听器模式,是观察者模式的变种,但是总体还是一样的啦。
下面简单实现一个监听器模式,有个全局概念先。
监听器模式要素
-
事件
事件,是被监听的对象。
-
监听器
负责监听感兴趣的事件。
-
广播器
负责广播事件。维护一个监听器集合。
广播到所有监听器,监听器发现有自己感兴趣的事件,就执行某些逻辑。
定义事件
-
事件抽象类
public abstract class DailyLlifeEvent { public abstract String getBehavior(); } 复制代码
-
放屁事件
public class FangPiEvent extends DailyLlifeEvent {
@Override
public String getBehavior() {
return "fangqi";
}
}
复制代码
- 拉屎事件
public class LaShiEvent extends DailyLlifeEvent {
@Override
public String getBehavior() {
return "lashi";
}
}
复制代码
定义监听器
-
监听器接口
public interface DailyLiferListener { void onDailyLlifeEvent(DailyLlifeEvent event); } 复制代码
-
放屁监听器
@Component public class FangPiListener implements DailyLiferListener { @Override public void onDailyLlifeEvent(DailyLlifeEvent event) { if (event instanceof LaShiEvent) { System.out.println("家里的所有人注意:你"+event.getBehavior()+"了"); } } } 复制代码
-
拉屎监听器
@Component public class LaShiListener implements DailyLiferListener { @Override public void onDailyLlifeEvent(DailyLlifeEvent event) { if (event instanceof LaShiEvent) { System.out.println("化粪池注意:你"+event.getBehavior()+"了"); } } } 复制代码
定义广播器
-
广播器接口
public interface EventMulticaster { void multicastEvent(DailyLlifeEvent event); void addListener(DailyLiferListener dailyLiferListener); void removeListener(DailyLiferListener dailyLiferListener); } 复制代码
-
广播器抽象类
这里还用到了模板方法,不难理解。
@Component public abstract class AbstractEventMulticaster implements EventMulticaster { // 这里是把子类监听器注入 @Autowired private List<DailyLiferListener> listenerList; @Override public void multicastEvent(DailyLlifeEvent event) { doStart(); listenerList.forEach(i -> i.onDailyLlifeEvent(event)); doEnd(); } @Override public void addListener(DailyLiferListener dailyLiferListener) { listenerList.add(dailyLiferListener); } @Override public void removeListener(DailyLiferListener dailyLiferListener) { listenerList.remove(dailyLiferListener); } abstract void doStart(); abstract void doEnd(); } 复制代码
-
广播器实现类
@Component public class DailyLifeEventMulticaster extends AbstractEventMulticaster { @Override void doStart() { System.out.println("--开始广播开始广播--"); } @Override void doEnd() { System.out.println("--结束广播结束广播--"); } @PostConstruct public void postConstruct() { this.addListener(new FangPiListener()); this.addListener(new LaShiListener()); } } 复制代码
效果
--开始广播开始广播--
家里的所有人注意:你fangqi了
不感兴趣的事件已略过
--结束广播结束广播--
复制代码
Spring Boot 中的监听器
设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
复制代码
还是类似的方法,我们直接到 Spring Boot 的 spring.factories
文件查看 ApplicationListener
配置了哪些监听器。
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码
看看我们发现了啥,一个熟悉的名字 DelegatingApplicationListener
,上一节学习 系统初始化器 的时候,我们就见过类型的命名DelegatingApplicationContextInitializer
。
如果咱们没猜错的话,他的作用也是类似的,加载配置中的 listener,然后消费一波。让我们看看源码。
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
private static final String PROPERTY_NAME = "context.listener.classes";
private int order = 0;
private SimpleApplicationEventMulticaster multicaster;
// 哼哼,被我猜对了。
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 首先监听了 ApplicationEnvironmentPreparedEvent 事件
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 此事件发生后,加载环境中配置的 listeners
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return;
}
// 把 listeners 中放到 multicaster
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);
}
}
// 然后再发布一波事件,让刚才的 listener 监听到。
if (this.multicaster != null) {
this.multicaster.multicastEvent(event);
}
}
@SuppressWarnings("unchecked")
private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
if (environment == null) {
return Collections.emptyList();
}
String classNames = environment.getProperty(PROPERTY_NAME);
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
try {
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationListener.class, clazz,
"class [" + className + "] must implement ApplicationListener");
listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
}
catch (Exception ex) {
throw new ApplicationContextException("Failed to load context listener class [" + className + "]",
ex);
}
}
}
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END