Освоение обработки событий Spring

Spring

头图

1. Введение

На этот раз мы узнаем об обработке событий Spring, которая вытекает из требований проекта, встречающихся в реальной работе: в сценарии платежного поручения, когда пользователь действительно успешно платит, серверу необходимо своевременно обновлять статус данных заказа после получения обратного вызова. для обеспечения согласованности данных. Обычной практикой является использование службы заказа для обновления данных непосредственно в методе обратного вызова, однако таким образом два модуля тесно связаны между собой. необходимо изменить.

Чтобы этого не произошло, я использовал метод публикации и подписки событий Spring для реализации функции приема платежных обратных вызовов и публикации уведомлений для обновления статуса заказа, чтобы работа сервиса заказа по обновлению данных зависела только от конкретных событий , и не заботится о конкретных триггерах. Объекты также могут выполнять задачу повторного использования кода.

Основное содержание этой статьи следующее:

  • Обработка стандартных событий Spring
  • Реализация пользовательского расширения событий в Spring
  • События и слушатели Spring Boot

Пример проекта:

Экологическая поддержка:

  • JDK 8
  • SpringBoot 2.1.4
  • Maven 3.6.0

2.1 Стандартная обработка событий Spring

В процессе запуска программы Spring будут появляться различные уведомления о событиях, 5 встроенных стандартных событий:

событие иллюстрировать
ContextRefreshedEvent Запускается, когда контейнер Spring находится в фазе инициализации или обновления, фактApplicationContext#refresh()Когда метод вызывается, контейнер уже инициализирован.
ContextStartedEvent при звонкеConfigurableApplicationContextпод интерфейсомstart()Метод срабатывает, указывая на то, что контейнер Spring запущен; обычно он используется для запуска после явного закрытия контейнера Spring.
ContextStoppedEvent при звонкеConfigurableApplicationContextпод интерфейсомstop()метод срабатывает, указывая на то, что контейнер Spring остановлен, и его можно передать через егоstart()способ перезапустить контейнер.
ContextClosedEvent Когда контейнер Spring вызываетApplicationContext#close()метод, после чего bean-компоненты Spring были уничтожены и не будут перезапущены и обновлены.
RequestHandledEvent Существует только в веб-приложениях, он будет запущен при получении и обработке HTTP-запроса, класс реализации по умолчанию фактически переданServletRequestHandledEvent

Обычно программы Spring получаютContextRefreshedEvent, ContextClosedEventоповещение о событиях.

Зная о событиях, которые приходят с Spring, мы можем использовать механизм событий для удовлетворения требований некоторых сценариев, таких как инициализация ресурсов после запуска Spring, загрузка кэшированных данных в память и так далее. Реализация кода также очень проста:

@Component
public class InitalizeListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        System.out.println("Spring 容器启动  获取到 Application Context 对象 " + applicationContext);
        //TODO 初始化资源,加载缓存数据到内存
    }
}

// 启动 Spring 程序后,控制台出现如下日志:
// Spring 容器启动  获取到 Application Context 对象 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6950ed69, started on Sun May 26 12:19:33 CST 2019

мы можем начать сContextRefreshedEventполучено от событияApplicationContextобъект, чтобы получить любой загруженный компонент в контейнере Spring для пользовательских операций.

2.1.1 Слушатели событий, управляемые аннотациями

Представьте @EventListener

Начиная с Spring 4.2, Spring предоставляет более гибкий метод обработки прослушивателя событий на основе аннотаций. В основном используется@EventListenerАннотация для обозначения методов, которые должны прослушивать события программы, нижний слойEventListenerMethodProcessorОбъект преобразует метод аннотации вApplicationListenerпример.

Почему этот метод аннотации более гибкий для прослушивания событий?Мы можем сначала посмотреть на него@EventListenerАннотированный исходный код.

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {

    @AliasFor("classes")
    Class<?>[] value() default {};

    @AliasFor("value")
    Class<?>[] classes() default {};

    String condition() default "";
}

EventListenerАннотации имеют два основных свойства:classesа такжеcondition.classesУказывает тип события, которое необходимо прослушивать, которое представляет собой массив, поэтому разрешено прослушивать несколько различных событий в одном методе, чтобы достичь эффекта мультиплексирования;conditionКак следует из названия, он используется для определения предварительных условий обработки события прослушивания.Здесь следует отметить, что использованиеSpring Expression Language(SpEL) определяет такие условия, как#root.eventвыражает конкретныйApplicationEventобъект, вы можете обратиться к следующему примеру кода для использования:

@Component
public class AnnotationListener {

    @EventListener(value = {ContextRefreshedEvent.class, ContextStartedEvent.class, ContextStoppedEvent.class, ContextClosedEvent.class, RequestHandledEvent.class}, condition = "#root.event != null")
    public void listener(ApplicationEvent event) {
        System.out.println(Thread.currentThread() + " 接收到 Spring 事件:" + event);
    }
}

Здесь следует отметить, что аннотация@EventListenerОтмеченные типы параметров метода больше не ограничиваются обязательными.ApplicationEventПодкласс , который не реализуетApplicationListenerОграничения интерфейсных методов также делают события более гибкими.

доставка событий

Кроме того, используйте@EventListenerОн также поддерживает доставку событий, инкапсулирует результаты обработки текущего события и публикует новое событие.nullзначение, считается, что событие продолжает распространяться, как в следующем примере кода:

@Component
@Order(2)
public class CustomEventListener {
    @EventListener
    public SecondCustomEvent listener(CustomEvent event) {
        System.out.println(Thread.currentThread() + "CustomEventListener接受到自定义事件:" + event);
        return new SecondCustomEvent(this, event.toString());
    }
}

2.1.2 Приоритет слушателя

Когда у нас есть несколько слушателей для одного события, мы можем захотеть указать порядок выполнения слушателей из-за требований Spring также принял это во внимание для нас, пока мы используем@OrderАннотация объявляет класс слушателя или метод слушателя в соответствии с@OrderизvalueРазмер определяет порядок выполнения, чем меньше приоритет.

@EventListener
@Order(42)
public void processEvent(Event event) {
}

2.2 Пользовательские события

Поняв, как прослушивать события Spring, давайте посмотрим, как реализовать публикацию пользовательских событий и обработку прослушивания. Прежде всего, мы представим три типа объектов механизма событий в Spring:

  • Event: конкретный объект события, который необходимо инициировать, обычно расширенныйApplicationEventвыполнить.
  • Publisher: Объект, запускающий публикацию событий, Spring предоставляетApplicationEventPublisherобъект для нас, чтобы использовать,publishEvent()способ публикации события.
  • Listener: объект, который прослушивает возникновение события, то есть место, где обратный вызов принимается для обработки, что может быть достигнуто путемApplicationListenerинтерфейс, или используйте ранее упомянутый@EventListenerАннотация объявляется прослушивателем события.

Далее давайте кратко рассмотрим пример кода пользовательского события от объявления до публикации и подписки.

2.2.1 НастройкаApplication Event

public class CustomEvent extends ApplicationEvent {
    private String data;

    public CustomEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "CustomEvent{" +
                "data='" + data + '\'' +
                ", source=" + source +
                '}';
    }
}

2.2.2 Настройка издателя

@Component
public class CustomeEventPublisher implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publishEvent(String message) {
        System.out.println("开始发布事件 " + message);
        applicationEventPublisher.publishEvent(new CustomEvent(this, message));
    }
}

Существует два способа создания издателя событий. Один из них — использовать@AutowireАннотация, введенная напрямую через функцию внедрения зависимостей контейнера Spring.ApplicationEventPublisherобъект или реализацияApplicationEventPublisherAwareИнтерфейс, устанавливаемый Spring при запуске контейнера Spring.

2.2.3 Пользовательский прослушиватель

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println(Thread.currentThread()+"CustomEventListener接受到自定义事件:" + event);
    }
}

При определении прослушивателя событий мы делаем это, реализуяApplicationListenerИнтерфейс определяет тип события, поэтому при обработке события нельзя избежать оценки и преобразования типа события.

Еще одна вещь, которую следует отметить в отношении прослушивателей событий, заключается в том, что обработка событий Spring по умолчанию является синхронной, что упоминается в официальных документах Spring.Давайте сначала интерпретируем официальное описание:

You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously. This means that the publishEvent()метод блокируется до тех пор, пока все прослушиватели не закончат обработку события. Одно из преимуществ этого синхронного и однопоточного подхода заключается в том, что когда прослушиватель получает событие, он работает внутри контекста транзакции издателя, если контекст транзакции доступен. для публикации события становится необходимым, см. javadoc для SpringApplicationEventMulticaster interface.

Когда издатель выполняетpublishEvent()метод, по умолчанию текущий поток, в котором находится метод, будет заблокирован до тех пор, пока все слушатели, связанные с событием, не закончат обработку события. Преимущество использования однопоточной синхронизации таким образом заключается в том, что обработка событий и издатель находятся в одной и той же среде транзакций.Если несколько методов прослушивания включают операции с базой данных, существование транзакций гарантируется.

2.2.4 Асинхронная обработка событий

Конечно, Spring также предоставляет способ асинхронного прослушивания событий, который в основном зависит отApplicationEventMulticasterИнтерфейс можно понимать как широковещательный метод.Для простоты использования Spring предоставляет простой класс реализацииSimpleApplicationEventMulticasterДля нашего прямого использования нам нужно только зарегистрировать этот объект в контейнере Spring.

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster
                = new SimpleApplicationEventMulticaster();
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

здесьApplicationEventMulticasterБин нуждается вjava.util.concurrent.ExecutorОбъекты используются как пулы потоков для обработки событий, мы напрямую используем те, которые предоставляет Spring.SimpleAsyncTaskExecutorObject, новый поток создается каждый раз, когда обрабатывается событие.

Примечание: ЗарегистрируйтесьApplicationEventMulticasterПосле bean-компонента вся обработка прослушивания событий станет асинхронной.Если вам нужно использовать асинхронный метод для прослушивания определенного события: вы можете использовать@EventListenerа также@Asyncсочетание для достижения. (Предполагается, что программа Spring включена@EnableAsyncаннотация)

Вот плюсы и минусы использования асинхронных методов для обработки событий.Преимущество в том, что наша программа более эффективно обрабатывает события, а недостаток в том, что обработка исключений более сложная и требует помощиAsyncUncaughtExceptionHandlerреализация интерфейса.

2.3 События Spring Boot и прослушивание

Узнав так много об обработке событий Spring Framework, давайте теперь посмотрим, какие дополнительные знания необходимы для обработки событий в Spring Boot. Тем не менее, давайте начнем с официальной документации Spring Boot, в Spring Boot

Дока23.5 Application Events and ListenersОбработка событий упоминается в разделе:

  • In addition to the usual Spring Framework events, such as ContextRefreshedEvent, a SpringApplication sends some additional application events.

  • События приложения отправляются с помощью механизма публикации событий Spring Framework.

Видно, что Spring Boot по-прежнему основан на механизме публикации событий Spring Framework для обработки событий, но на этой основе было добавлено несколько новых.SpringApplicationСвязанные события:

  • ApplicationStartingEvent: Происходит при запуске программы.
  • ApplicationEnvironmentPreparedEvent: в программеEnvironmentПроисходит, когда объект готов.
  • ApplicationPreparedEvent: Происходит после того, как программа запущена, но не обновлена.
  • ApplicationStartedEvent: Происходит после обновления запуска программы.
  • ApplicationReadyEvent: Происходит, когда программа запущена и ожидает запроса.
  • ApplicationFailedEventНенормальный происходит во время запуска программы.

И порядок их выполнения также определяется порядком перечисления книг.

Кроме того, следует отметить, что когда событие, которое необходимо инициировать, находится вApplicationContextпроисходит до создания, с@BeanСлушатели, зарегистрированные таким образом, не будут выполняться, и Spring Boot предоставляет три способа справиться с этой ситуацией:

  1. использоватьSpringApplication.addListeners(…)слушатель регистрации метода

    SpringApplication springApplication = new SpringApplication(SpringEventsApplication.class);
    springApplication.addListeners(new NormalCustomEventListener());
    springApplication.run(args);
    
  2. использоватьSpringApplicationBuilder.listeners(…)слушатель регистрации метода

    SpringApplicationBuilder springApplicationBuilder = new SpringApplicationBuilder(SpringEventsApplication.class);
    springApplicationBuilder.listeners(new NormalCustomEventListener()).run(args);
    
  3. Создайте новый файл в папке ресурсов приложенияMETA-INF/spring.factories, и воляorg.springframework.context.ApplicationListenerВ качестве ключа укажите класс слушателя, который необходимо зарегистрировать, например:

    org.springframework.context.ApplicationListener=\
    com.one.learn.spring.springevents.listener.NormalSecondCutomEventListener
    

3 Заключение

На этом мы закончили изучение событий Spring.Понимание механизма событий Spring и его правильное применение может предоставить нам более несвязанный и гибкий метод реализации, когда мы выполняем определенную функцию программы.

Если вы чувствуете, что получили что-то после прочтения, нажмите [Хорошо выглядит], нажмите на изображение заголовка статьи и отсканируйте код, чтобы подписаться на [технический блог Венрена] 😄😄😄.

4 Ссылка

Spring context-functionality-events: docs.spring.IO/весна/документы…

Spring boot-features-application-events-and-listeners:docs.spring.IO/весенняя загрузка…

Spring Expression Language: docs.spring.IO/весна/документы…

SpringEvents: Ву Ву Принеси Arlington Terriers.com/spring-even…

Better application events in Spring Framework 4.2: весна.IO/блог/2015/0…