Новички в исходном коде SpringBoot (2): прослушиватель событий SpringBoot

Spring Boot

ps: Объяснение исходного кода SpringBoot, которое действительно подходит для начинающих, которые читают исходный код.Если вы действительно хотите понять исходный код SpringBoot, вы можете прочитать статью следующим рекомендуемым способом

  1. Откройте ide, откройте исходный код SpringBoot, напишите комментарии к статье и напишите свои комментарии.
  2. Не стоит слишком запутываться в местах, которые не были упомянуты, ведь исходников SpringBoot очень много, всю историю закончить невозможно, пока вы следите за статьей и внимательно читаете, вы должны иметь более глубокое понимание того, как работает SpringBoot.
  3. Статья подходит для чтения насквозь, а не для пропуска, пропуская чтение легко пропустить важные вещи
  4. Точно так же, если вы не читали предыдущую статью, лучше сначала прочитать предыдущую статью.
  5. Чтение исходного кода неизбежно приведет к большому разделу исходного кода, вы должны быть терпеливы, не просматривайте вещи, часто самые длинные методы - это те, которые действительно нужно изучить.
  6. Если он сломается, мотивируйте меня лайками, добавлением в избранное и комментариями.

Ссылки на серию статей:«Новички с исходным кодом 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");
    }
}