ps: Объяснение исходного кода SpringBoot, которое действительно подходит для начинающих, которые читают исходный код.Если вы действительно хотите понять исходный код SpringBoot, вы можете прочитать статью следующим рекомендуемым способом
- Откройте ide, откройте исходный код SpringBoot, напишите комментарии к статье и напишите свои комментарии.
- Не стоит слишком запутываться в местах, которые не были упомянуты, ведь исходников SpringBoot очень много, всю историю закончить невозможно, пока вы следите за статьей и внимательно читаете, вы должны иметь более глубокое понимание того, как работает SpringBoot.
- Статья подходит для чтения насквозь, а не для пропуска, пропуская чтение легко пропустить важные вещи
- Точно так же, если вы не читали предыдущую статью, лучше сначала прочитать предыдущую статью.
- Чтение исходного кода неизбежно приведет к большому разделу исходного кода, вы должны быть терпеливы, не просматривайте вещи, часто самые длинные методы - это те, которые действительно нужно изучить.
- Если он сломается, мотивируйте меня лайками, добавлением в избранное и комментариями.
Ссылки на серию статей:«Новички с исходным кодом SpringBoot (1): использование интерфейса расширения функций SpringBoot и анализ исходного кода»
Режим монитора
Следуйте некоторым принципам на пути обучения, вы сможете учиться эффективнее, есть такое «шаг за шагом», прежде чем погрузиться в SpringBoot, вы должны сначала понять, что такое слушатель и как реализован слушатель. Король демонов, как и в ролевой игре, перед тем, как победить босса, вы должны сразиться с мобами, чтобы повысить свой уровень, и будет выпущен «Меч убийцы драконов». Эзреаль, как исследователь имени группы на континенте Валола, был в пути, чтобы исследовать, но на него всегда влияла погода, и он не мог рисковать, поэтому он попросил меня помочь ему написать программное обеспечение, чтобы помочь ему сосредоточиться. к погоде.
1. Небольшая демонстрация режима прослушивания! монитор погоды
Шаг 1: Создайте абстрактный класс WeatherEvent (состояние погоды)
public abstract class weatherEvent{
//获取天气状态
public abstract String getWeather();
}
Шаг 2: Реализуйте события снега и дождя снежное событие
public class SnowEvent extends WeatherEvent{
@Overide
public String getWeather(){
return "下雪了";
}
}
событие дождя
public class RainEvent extends WeatherEvent{
@Overide
public String getWeather(){
return "下雨了";
}
}
Шаг 3: Создайте интерфейс прослушивателя погоды
public interface WeatherListener{
void onWeatherEvent(WeatherEvent event);
}
Шаг 4. Внедрите прослушиватели для раздельной обработки снежной и дождливой погоды. Когда идет снег, вам нужно надеть большую ватную куртку и перчатки, чтобы не замерзнуть.
public class SnowListener implements WeatherListener{
@Override
public void onWeatherEvent(WeatherEvent event){
if(event instanceof SnowEvent){
event.getWeather();
System.out.println("今天下雪!请增加衣物,做好御寒保护!");
}
}
}
Возьмите с собой зонт и резиновые сапоги, когда идет дождь
public class RainListener implements WeatherListener{
@Override
public void onWeatherEvent(WeatherEvent event){
if(event instanceof RainEvent){
event.getWeather();
System.out.println("今天下雨!出门请带好雨伞");
}
}
}
Шаг 5: Создайте интерфейс вещателя
public interface EventMulticaster{
//广播事件
void multicastEvent(WeatherEvent event);
//添加监听器
void addListener(WeatherListener weaterListener);
//删除监听器
void removeListener(WeatherListener weaterListener);
}
Шаг 6: Абстрактный класс реализует широковещательный интерфейс
public abstract class AbstractEventMulticaster implements EventMulticaster{
//存放监听器的集合,所有需要监听的事件都存在这里
private List<WeaterListener> listenerList = new ArrayList<>();
@Override
public void multicastEvent(WeatherEvent event){
//采用模板方法,子类可以实现的doStart和doEnd,在调用监听器之前和之后分别作出扩展
//SpringBoot中有着大量相似的操作
//SpringBoot中的前置处理器和后置处理器,就是这样实现的
doStart();
//循环所有调用所有监听器的onWeatherEvent方法
listenerList.forEach(i -> i.onWeatherEvent(evnet));
doEnd();
}
@Override
public void addListener(WeatherListener weaterListener){
listenerList.add(weaterListener);
}
@Override
public void removeListener(WeatherListener weaterListener){
listenerList.remove(weaterListener);
}
abstract void doStart();
abstract void doEnd();
}
Шаг 7: Реализуйте трансляцию погодных явлений
public class WeatherEventMulticaster extends AbstractEventMulticaster{
@Override
void doStart(){
System.out.println("开始广播天气预报!");
}
@Override
void doEnd(){
System.out.println("广播结束!Over!");
}
}
Шаг 8: Протестируйте и запустите трансляцию
public class Test{
public static void main(String[] args){
//创建广播器
WeatherEventMulticaster eventMulticaster = new WeatherEventMulticaster();
//创建监听器
RainListener rainListener = new RainListener();
SnowListener snowListener = new SnowListener();
//添加监听器
eventMulticaster.addListener(rainListener);
eventMulticaster.addListener(snowListener);
//触发下雨事件
eventMulticaster.multicastEvent(new RainEvent());
//除非下雪事件
eventMulticaster.multicastEvent(new SnowEvent());
}
}
2. Лекционный зал Heimerdinger, объяснение механизма режима слушателя
Экспедиции Эзреаля наконец-то перестала беспокоить погода, но он не понимает, почему мелочи такие волшебные.Он меня много раз спрашивал, но плут, у меня язык плохой, и я не могу выражать такие сложные мысли Просто попросите старого друга Хеймердингера помочь объяснить.
ps:В работе надо не только осознавать функцию,но и обращать внимание на способность к самовыражению.Вы можете получить более высокую зарплату,когда сможете четко излагать свои мысли во время собеседования.При общении с тестом можно помогите тесту понять принцип реализации и протестировать скрытое Баги в недрах конечно как у гениальных программистов багов не бывает Это должно быть вызвано проблемами окружающей среды или неправильной эксплуатацией.
Хеймердингер получил код и простым взглядом проанализировал функцию каждого модуля:
- событие: Шаг 1 и Шаг 2, абстрагируясь от погоды и осознавая погодное состояние дождя и снега.
- слушатель: Шаги 3 и 4, стандартизируйте режим наблюдения за погодой и как бороться с соответствующей погодой
- телеведущий: Шаг 5, Шаг 6 и Шаг 7, когда происходит событие, вещательная компания отправляет сигнал для информирования всех слушателей, и слушатели выполняют соответствующую обработку в соответствии с событием. Когда сработало событие дождя, дождевик получил сообщение, посмотрел на темные тучи, покрытые молниями и громом, на мгновение остолбенел и закричал: «Дождь идет, собирай одежду!!», - продолжил вещатель. чтобы уведомить следующего Слушатель Снежный слушатель, Снежный слушатель Посмотри на небо, помаши рукой и скажи: «Это не мое дело, иди и найди кого-нибудь другого».
- спусковой механизм: На шаге 8 он запускается жестко закодированной формой, используемой в демонстрации.На практике может случиться так, что гигрометр обнаруживает, что влажность резко возросла, и начинается дождь, запуская трансляцию.
Среди 23 шаблонов проектирования нет шаблона слушателя. Шаблон слушателя является реализацией шаблона наблюдателя. Эти два названия легко ввести людей в заблуждение. В «слушании» и «наблюдении» легко почувствовать, что это прослушиватель, который обнаруживает событие и затем действует. Фактически, вещатель сообщает о событии всем слушателям, и каждый слушатель оценивает и обрабатывает событие.
Во-вторых, реализация прослушивателя событий SpringBoot.
1. Интерфейс прослушивателя приложений
ApplicationListener является частью механизма событий Spring. Он взаимодействует с абстрактным классом ApplicationEvent для завершения механизма событий ApplicationContext. Класс, реализующий интерфейс ApplicationListener, будет добавлен к вещателю в SpringBoot. Когда событие инициируется ApplicationContext, вещатель будет использоваться для уведомления всех реализаций Класс для интерфейса ApplicationListener.
//这个注解表示,当前类只有一个方法
@FunctionalInterface
//传入的泛型,说明这个监听器,需要监听的事件类型
//继承的EventListener类,是个空类,主要是声明继承它的类是个事件监听器,面向对象编程的思想体现
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
Нетрудно заметить, что интерфейс ApplicationListener почти такой же, как реализованный нами прослушиватель погоды на шаге 3. Если вы понимаете роль небольшого демо-класса, вы, должно быть, поняли это ясно.
2. Интерфейс ApplicationEventMulticaster
ApplicationEventMulticaster — это интерфейс вещателя механизма событий Spring. Все вещатели должны реализовать этот интерфейс. Основная функция — управлять всеми слушателями и отправлять события слушателям.
public interface ApplicationEventMulticaster {
//添加一个监听器
void addApplicationListener(ApplicationListener<?> listener);
//根据beanName添加一个监听器
void addApplicationListenerBean(String listenerBeanName);
//移除一个监听器
void removeApplicationListener(ApplicationListener<?> listener);
//根据beanName移除一个监听器
void removeApplicationListenerBean(String listenerBeanName);
//移除所有监听器
void removeAllListeners();
//广播事件的方法
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
3. 7 событий SpringBoot
[Не удалось передать изображение по внешней ссылке, исходный сайт может иметь механизм защиты от пиявки, рекомендуется сохранить изображение и загрузить его напрямую (img-cu6pwkNo-1585491460589)(en-resource://database/2523:1)]
- EventObject: объект верхнего уровня события, корневой объект всех объектов событий.
- ApplicationEvent: событие приложения
- SpringApplicationEvent: собственные события Spring, собственные события Spring Framework будут реализовывать этот интерфейс.
- ApplicationStartingEvent: стартовое событие, это событие будет сгенерировано сразу после запуска фреймворка.
- ApplicationEnvironmentPreparedEvent: среда меняется, загружаются системные свойства и пользовательские спецификации.
- ApplicationContextInitializedEvent: это событие генерируется до создания контекста и загрузки bean-компонентов.
- ApplicationPreparedEvent: запускается после того, как определение bean-компонента начинает загружаться, но до его полной загрузки, до обновления контекста.
- ApplicationStartedEvent: bean-компонент был создан, и контекст был обновлен, но два расширенных интерфейса ApplicationRunner и CommandLineRunne не были выполнены.
- ApplicationReadyEvent: запускается после выполнения двух расширенных интерфейсов ApplicationRunner и CommandLineRunne.
- ApplicationFailedEvent: срабатывает при возникновении исключения при запуске.
(1) Последовательность событий
Startup — ApplicationStartingEvent — ApplicationEnvironmentPreparedEvent — ApplicationContextInitializedEvent — ApplicationPreparedEvent — ApplicationStartedEvent — ApplicationReadyEvent — Запуск завершен
Произошло исключение в середине — «ApplicationFailedEvent —» запуск не удался
4. Анализ исходного кода прослушивателей событий
(1) Процесс регистрации слушателя
Если вы читали предыдущую статью
«Начинающие с исходным кодом SpringBoot (1): использование интерфейса расширения функций SpringBoot и анализ исходного кода»:https://juejin.cn/post/6844904106843193357
Здесь легко разобраться, если не хотите читать полностью, можете просто прочитать工厂加载机制源码解析
часть
Он регистрируется в том же процессе, что и интерфейс ApplicationContextInitializer, за исключением того, чтоApplicationContextInitializer接口
заменяетсяApplicationListener接口
Давайте рассмотрим его шаг за шагом с самого начала основного метода. Шаг 1. Просмотрите класс запуска SpringBoot.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//进入run方法的源码
SpringApplication.run(Application.class, args);
}
}
Шаг 2: Здесь вы можете увидеть слой простых вызовов
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
//进入这个同名方法,继续戳run方法
return run(new Class<?>[] { primarySource }, args);
}
Шаг 3: Это интереснее, обратите внимание на комментарии
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
//点这个SpringApplication构造方法
return new SpringApplication(primarySources).run(args);
}
Шаг 4: Бесполезная инкапсуляция, повторное использование составляющих функций
public SpringApplication(Class<?>... primarySources) {
//点this,查看构造函数
this(null, primarySources);
}
步骤5:
Здесь мы видим два знакомых имени: метод getSpringFactoriesInstances и интерфейс ApplicationContextInitializer.
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();
//这里就是上一篇文章说的ApplicationContextInitializer接口注册
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//这里就是ApplicationListener注册的位置,可以看出主要区别就是查询的接口类不同
//setListeners是找到的对象存到容器中,存到一个list属性中,方便以后使用
//这个存放对象的list,对应的是小demo的AbstractEventMulticaster类中list,作用是一样一样的
//getSpringFactoriesInstances方法详解参考文章《SpringBoot功能扩展接口的使用与源码分析》
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
(2) Процесс запуска слушателя
Шаг 1. Просмотрите класс запуска SpringBoot.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Шаг 2: класс ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
Шаг 3: На этот раз введите метод запуска
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
//点击run方法
return new SpringApplication(primarySources).run(args);
}
Шаг 4: Каждый раз, когда я вижу этот метод, я чувствую, что он грешный.Сколько людей начинают с него и встают на путь невозврата, чтобы прочитать исходный код
Код длинный, поэтому в этот раз я не буду писать все комментарии, конкретные комментарии смотрите здесьhttps://juejin.cn/post/6844904106843193357
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取事件运行器
//SpringApplicationRunListeners内部包含一个SpringApplicationRunListener(这里s没有了)的集合
//SpringApplicationRunListener有7大事件的执行方法,在对应的地点会被调用,SpringBoot通过这个实现事件的触发
//SpringBoot自带一个实现,这个实现分别会执行定义好的7大事件
//使用者可以通过实现SpringApplicationRunListener的接口,定义在对应事件所需执行的命令
//总体流程还是很简单的,留给大家自己阅读
SpringApplicationRunListeners listeners = getRunListeners(args);
//监听器的故事从这里开始,我们这次的故事也从这里起航
//进入starting方法
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;
}
Шаг 5: Никакой рутины Мелалеуки
public void starting() {
//listeners里面存放了所有的SpringApplicationRunListener(事件触发器)
for (SpringApplicationRunListener listener : this.listeners) {
//循环执行事件触发器的starting方法
//点击进入看看SpringBoot自带的事件触发器是如何运行的
listener.starting();
}
}
Шаг 6. Вещательная компания отправляет событие
@Override
public void starting() {
//initialMulticaster是广播器
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
Шаг 7. Вещательная компания отправляет событие
@Override
public void starting() {
//initialMulticaster是广播器
//进入multicastEvent方法
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
Шаг 8: При трансляции события необходимо определить тип события и определить, нужно ли его выполнять в данный момент времени.
@Override
public void multicastEvent(ApplicationEvent event) {
//resolveDefaultEventType方法,解析事件的默认类型
//进入resolveDefaultEventType方法,步骤9
//进入multicastEvent方法,步骤11
multicastEvent(event, resolveDefaultEventType(event));
}
Шаг 9: Получите тип события
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
//获取事件类型
//进入forInstance方法,步骤10
return ResolvableType.forInstance(event);
}
Шаг 10: Определите тип времени через интерфейс
public static ResolvableType forInstance(Object instance) {
//断路判断,如果instance是个空,就停止SpringBoot的启动,并报错
Assert.notNull(instance, "Instance must not be null");
//判断有没有实现ResolvableTypeProvider这个接口
//ResolvableTypeProvider接口,表明这个类的事件类型可以被解析
if (instance instanceof ResolvableTypeProvider) {
//强转成ResolvableTypeProvider类型,然后获取事件类型
ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
if (type != null) {
//事件类型不为空,就直接返回
return type;
}
}
//返回一个默认类型,传进来的instance是什么类型,就把这个类型包装成ResolvableType,然后返回
//返回步骤8
return ResolvableType.forClass(instance.getClass());
}
步骤11:
начать трансляцию
Два параметра: event: событие, которое необходимо выполнить eventType: тип события
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
//如果事件类型为空,执行resolveDefaultEventType方法(步骤9和步骤10)
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//获取任务的执行的线程池
//如果没有特别指定,返回为null,SpringBoot这里就是空的
Executor executor = getTaskExecutor();
//getApplicationListeners方法,获取对这个事件感兴趣的监听器
//点击进入getApplicationListeners方法,进入步骤12
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//在指定线程上执行触发
executor.execute(() -> invokeListener(listener, event));
}
else {
//默认方式执行触发
invokeListener(listener, event);
}
}
}
步骤12:
Заинтересовать слушателей в этом событии (логика кэширования выборки)
Описание параметра:
событие: текущее событие, этот метод должен найти слушателя, заинтересованного в этом событии
тип события: тип события
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
//获取事件发生的源头类,这里就是SpringApplication
Object source = event.getSource();
//获取原头类的类型
Class<?> sourceType = (source != null ? source.getClass() : null);
//获取缓存的key
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)))) {
//通过key上锁,这是上锁的一个很有效的方式,定义一个属性作为锁的key
synchronized (this.retrievalMutex) {
//上锁之后再次检查,有没有其他地方触发了当前事件,把监听器的列表放入了缓存中
//写过双层验证的单例模式对这里不会陌生,主要原理是一样的
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
//返回对当前事件感兴趣的监听器
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
//真正的查找逻辑被封装在这里
//SpringBoot这种千层套路,是有规律可循的,这一次是缓存的封装,下一次是实际的调用
//我们编程的时候可以学习一下,比如封装缓存的查询,再去数据库,降低耦合度
//点retrieveApplicationListeners方法进入 步骤13
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
//存入缓存中
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
//不需要加锁的,并且不需要缓存的查询方式
//这个方法中有两处调用了retrieveApplicationListeners方法,在方法的内部对有无缓存,做了不同的处理
//个人观点:应该把内部的缓存逻辑移到这层中,否则耦合度依旧很高
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
步骤13
: Логика получения слушателя
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
//获取所有的监听器实例
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
//获取所有监听器的beanName
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
//对所有的监听器进行逐一的循环
for (ApplicationListener<?> listener : listeners) {
//判断监听器是否对这个事件感兴趣
//点击supportsEvent方法进入 步骤14
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
//如果监听器功能开启了缓存,就存到缓存中
retriever.applicationListeners.add(listener);
}
//不管有没有缓存都会存到这里
allListeners.add(listener);
}
}
//通过工厂方式,获取监听器,一般情况不会走这里
if (!listenerBeans.isEmpty()) {
//获取bean工厂
BeanFactory beanFactory = getBeanFactory();
//循环监听器beanName
for (String listenerBeanName : listenerBeans) {
try {
//更具beanName,获取监听器的类型
Class<?> listenerType = beanFactory.getType(listenerBeanName);
// 判断监听器是否对这个事件感兴趣
if (listenerType == null || supportsEvent(listenerType, eventType)) {
//获取bean实例,这个方法写作getBean,读作createBean
//这是ioc中非常重要的一块逻辑,当获取不到bean的时候,就会创建一个bean对象
//具体的我们在后续ioc源码分析的时候讲解
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
//也是判断是否有缓存的逻辑
if (retriever != null) {
//多一个判断是否单例的逻辑
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
}
else {
//原形bean这里,想起来以前有个组员说这个叫“多例”,最好还是叫“原型”
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
}
}
}
//进行排序,SpringBoot的常规操作了,根据Order接口或者注解进行排序
AnnotationAwareOrderComparator.sort(allListeners);
//对缓存进行一次刷新,把以前的结果清空,将这次运行的结果缓存
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
//返回获取到的监听器
//返回 步骤12
return allListeners;
}
步骤14
: определить, заинтересован ли слушатель в текущем событии.
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
//判断监听器,是否是GenericApplicationListener的子类
//starting的事件并不是其子类
//GenericApplicationListener使用了装饰器模式
//著名的装饰器模式是java中io流(inputStream这些)
//GenericApplicationListener中可以解析ApplicationListener接口中的泛型参数,接口如下:
//“ApplicationListener<E extends ApplicationEvent>”要是还想不起来,回头看一下上面小Demo中的使用,和对这个接口的介绍
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
//下面就变得简单了,虽然内部的判断很繁杂,总体只做了两件事情
//supportsEventType:判断监听器是否支持当前事件
//supportsSourceType:监听器是否对这个事件的发起来类感兴趣
//返回一个总的bool值,返回 步骤13
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
5. Пользовательский прослушиватель SpringBoot
(1) Впрыск через spring.factories
Шаг 1. Создайте прослушиватель и реализуйте интерфейс ApplicationListener.
//我们让这个监听器对ApplicationStartedEvent事件感兴趣
@Order(1)
public class TestListener implements ApplicationListener<ApplicationStartedEvent>{
@Ovrride
public void onApplicationEvent(ApplicationStartedEvent event){
System.out.println("hello, Application start is over");
}
}
Шаг 2. Добавьте рекомендации по реализации классов в spring.factories.
Речь идет о содержании предыдущей лекции, для тех, кто еще не знает, пожалуйста, нажмите здесь и поторопитесь изучить ее:https://juejin.cn/post/6844904106843193357
#com.gyx.test.Listener是刚刚写的监听器的全路径名
org.springframework.context.ApplicationListener=com.gyx.test.TestListener
Затем запустите программу, вы обнаружите, что появляется печатное заявление
(2) Ручная инъекция SpringApplication
Шаг 1: Создайте прослушиватель и реализуйте интерфейс ApplicationListener точно так же, как указано выше. Шаг 2. Измените класс запуска SpringBoot.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
//添加到初始化配置项中
springApplication.addListeners(new TestListener());
springApplication.run(args);
}
}
(3) Зарегистрируйтесь в файле конфигурации SpringBoot.
Шаг 1: Создайте прослушиватель и реализуйте интерфейс ApplicationListener точно так же, как указано выше. Шаг 2: Измените файл конфигурации
context.listener.classes=com.gyx.test.TestListener
Друзья, кто смотрел предыдущий урок, заметили ли вы, что он точно такой же, как метод регистрации ApplicationContextInitializer раньше! ! ! Вы немного чувствуете, куйте железо, пока горячо, давайте поговорим об этом и еще раз повторим
(4) Мониторинг нескольких событий, реализация интерфейса SmartApplicationListener
Этот метод реализует только разные интерфейсы, а метод внедрения один и тот же. Можно использовать три вышеуказанных метода внедрения. Шаг 1. Создайте прослушиватель и реализуйте интерфейс SmartApplicationListener.
@Order(1)
public class TestSmartListener implements SmartApplicationListener{
@Ovrride
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType){
//这里是类型判断,判断监听器感兴趣的事件
//可以对多个事件感兴趣,这里就配置了两个事件
return ApplicationStartedEvent.class.isAssignableFrom(eventType)
|| ApplicationPreparedEvent.class.isAssignableFrom(eventType);
}
@Ovrride
public void onApplicationEvent(ApplicationStartedEvent event){
System.out.println("hello, This is smartApplicationListener");
}
}