Рекомендации по облегченному артефакту развязки внутренних компонентов Spring Event (событие Spring)
Рекомендации по облегченному артефакту развязки внутренних компонентов Spring Event (событие Spring)

Рекомендации по передовой практике весенних мероприятий

  • Привет всем, я Лорин, в прошлой статье мы представили Spring Event Давайте рассмотрим базовое использование, основные принципы и сценарии адаптации в этой статье. Spring Event существуют Некоторыеиспользуются примеры и лучшие из реальных проектов практикирекомендовать。
  • В этой статье мы по-прежнему используем приведенное выше событие входа в систему для демонстрации.

Версия

  • JDK 8
  • Spring-boot 2.6.6

Пример события входа в систему

  • Ниже приведен простой пример использования событий Spring для обработки входа пользователя в систему. В этом примере мы создадим приложение Spring Boot, которое демонстрирует, как использовать события Spring для обработки событий входа пользователя в систему.

Создать событие входа в систему

  • Создайте собственный класс событий для представления событий входа пользователя, например LogonEvent:
Язык кода:java
копировать
public class LoginEvent extends ApplicationEvent {

    private final String userName;

    public LoginEvent(Object source, String username) {
        super(source);
        this.userName = username;
    }

    public String getUserName() {
        return userName;
    }
}

Создать издателя событий

  • Создайте публикатор событий для публикации событий входа пользователя в систему:
Язык кода:java
копировать
@Service
public class LoginEventPublisher {

    private final ApplicationEventPublisher applicationEventPublisher;

    public LoginEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publishLoginEvent(String username) {
        LogonEvent loginEvent = new LoginEvent(this, username);
        applicationEventPublisher.publishEvent(loginEvent);
    }
}

Создать прослушиватель событий

  • Создать прослушиватель событий,Используется для обработки событий входа пользователя в систему.,Поддерживает создание одной или нескольких похожих моделей публикации-подписки.,В этом примере создаются два прослушивателя времени:
Язык кода:java
копировать
// Прослушиватель событий обработки журнала
@Component
public class LoginEventPrintLogListener {

    @EventListener
    public void handleUserLoginEvent(LoginEvent event) {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий входа пользователя в систему, например, регистрация или запуск других действий.
        System.out.println("User logged in: " + username);
    }
}

// Прослушиватель событий уведомления о входе в систему
@Component
public class LoginEventMessageNoticeListener {

    @EventListener
    public void LoginEventMessageNoticeListener(LoginEvent event) {
        String username = event.getUserName();
        // Отправьте сообщение, чтобы уведомить пользователей
        System.out.println("message send User logged in: " + username);
    }
}

Имитировать вход пользователя

  • Для удобства тестирования используется CommandLineRunner для имитации входа в систему при запуске:
Язык кода:java
копировать
@Component
public class MyCommandLineRunner implements CommandLineRunner {

    private final LoginEventPublisher loginEventPublisher;

    public MyCommandLineRunner(LoginEventPublisher loginEventPublisher) {
        this.loginEventPublisher = loginEventPublisher;
    }

    @Override
    public void run(String... args) {
        // существует логика восстановления, выполняемая после запуска приложения
        System.out.println("MyCommandLineRunner executed!");

        // Вход успешен
        // Логика входа в систему после выполнения
        loginEventPublisher.publishLoginEvent("Сяо Ван");
    }
}

Результаты бега

Язык кода:java
копировать
2023-10-13 16:04:02.021  INFO 5356 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1250 ms
2023-10-13 16:04:02.382  INFO 5356 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-10-13 16:05:31.792  INFO 5356 --- [           main] c.e.s.SpringBootTestMavenApplication     : Started SpringBootTestMavenApplication in 200.49 seconds (JVM running for 201.165)

MyCommandLineRunner executed!
message send User logged in: Сяо Ван
User logged in: Сяо Ван

Один слушатель и несколько слушателей

  • В приведенном выше примере мы использовали несколько прослушивателей для мониторинга событий входа в систему. Если в нашем реальном бизнесе нам нужен только один прослушиватель, мы можем использовать один прослушиватель.
Язык кода:java
копировать
// Прослушиватель событий обработки журнала
@Component
public class LoginEventPrintLogListener {

    @EventListener
    public void handleUserLoginEvent(LoginEvent event) {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий входа пользователя в систему, например, регистрация или запуск других действий.
        System.out.println("User logged in: " + username);
    }
}

// Прослушиватель событий уведомления о входе в систему
@Component
public class LoginEventMessageNoticeListener {

    @EventListener
    public void LoginEventMessageNoticeListener(LoginEvent event) {
        String username = event.getUserName();
        // Отправьте сообщение, чтобы уведомить пользователей
        System.out.println("message send User logged in: " + username);
    }
}

Не использовать аннотации

  • Помимо использования аннотаций для реализации слушателей, мы также можем реализовать их без аннотаций:
Язык кода:java
копировать
@Component
public class LoginEventPrintLogListenerTest implements ApplicationListener<LoginEvent> {

    @Override
    public void onApplicationEvent(LoginEvent event) {
        System.out.println("this is a Listener with not annotation");
    }
}

Асинхронный прослушиватель событий

  • По умолчанию прослушиватель событий использует текущий поток для синхронной обработки событий. Текущий поток блокируется до завершения обработки событий. В некоторых сценариях, когда прослушиватель событий обрабатывает события в течение длительного времени, мы в этом случае не подходим. может использовать асинхронную обработку.
Язык кода:java
копировать
@SpringBootApplication
// включать Async
@EnableAsync
public class SpringBootTestMavenApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootTestMavenApplication.class, args);
    }
}

@Component
public class LoginEventPrintLogListener {

    @EventListener
    // Настроить задачу асинхронно Создайте новый поток для обработки события.
    @Async
    public void handleUserLoginEvent(LoginEvent event) throws Exception {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий входа пользователя в систему, например, регистрация или запуск других действий.
        System.out.println("User logged in: " + username);
    }
}

Условные события

  • Spring также предоставляет возможность условно обрабатывать события на основе определенных критериев. Это дает нам прекрасный контроль над обработкой событий слушателем.
  • Например, в следующем примере выполнение прослушивателя запускается только тогда, когда имя пользователя вошедшего в систему пользователя равно Сяо Вану:
Язык кода:java
копировать
@Component
public class LoginEventPrintLogListener {

    @EventListener(condition = "#event.userName.equals('Сяо Ван')")
    public void handleUserLoginEvent(LoginEvent event) throws Exception {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий входа пользователя в систему, например, регистрация или запуск других действий.
        System.out.println("User logged in: " + username);
    }
}

Транзакционные события

  • События Spring можно использовать вместе с транзакциями, но может возникнуть следующая ненормальная ситуация. После успешной регистрации пользователя высвобождается событие входа в систему, но при последующей обработке транзакции обрабатывается исключение, что приводит к откату транзакции. получить сообщение об успешной регистрации, но на самом деле регистрация не удалась.
  • Для приведенного выше сценария у нас обычно есть два решения:
Язык кода:txt
копировать
Вариант 1. Разделите логику обработки транзакций и публикации событий, чтобы избежать описанных выше ненормальных сценариев (рекомендуется).
Вариант 2:использовать TransactionalEventListener Укажите связь последовательности с выполнением транзакции.

@TransactionalEventListener

  • В Spring 4.2+ был введен @TransactionalEventListener для улучшения @EventListener. Чтобы иметь возможность контролировать обработку событий Event во время транзакций.
Язык кода:java
копировать
@Component
public class MyCommandLineRunner implements CommandLineRunner {

    private final RegisterEventPublisher registerEventPublisher;

    public MyCommandLineRunner(RegisterEventPublisher registerEventPublisher) {
        this.RegisterEventPublisher = registerEventPublisher;
    }

    @Override
    @Transactional
    public void run(String... args) {
        // существует логика восстановления, выполняемая после запуска приложения
        System.out.println("MyCommandLineRunner executed!");

        // Регистрация прошла успешно
        // Регистрация логики после выполнения
        registerEventPublisher.publishRegisterEvent("Сяо Ван");
        // Исключение выполнения вызывает откат транзакции
    }
}

@Component
public class RegisterEventPrintLogListener {

    // Транзакция будет выполнена после ее отправки
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserRegisterEvent(RigisterEvent event) throws Exception {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий регистрации пользователей, например ведение журнала или запуск других операций.
        System.out.println("User register: " + username);
    }
}

Order of Event Execution (порядок выполнения событий мониторинга)

  • По умолчанию, если несколько прослушивателей обрабатывают одно и то же событие, мы можем использовать аннотацию @Order, чтобы указать порядок выполнения.
Язык кода:java
копировать
@Component
public class LoginEventPrintLogListener {

    @EventListener
    @Order(1)
    public void handleUserLoginEvent(LoginEvent event) throws Exception {
        String username = event.getUserName();
        // существуют. Здесь выполняется логика обработки событий входа пользователя в систему, например, регистрация или запуск других действий.
        System.out.println("User logged in: " + username);
    }
}

@Component
public class LoginEventMessageNoticeListener {

    @EventListener
    @Order(2)
    public void LoginEventMessageNoticeListener(LoginEvent event) {
        String username = event.getUserName();
        // Отправьте сообщение, чтобы уведомить пользователей
        System.out.println("message send User logged in: " + username);
    }
}

Общие события

  • Мы также можем использовать дженерики для реализации общей обработки событий. Определите общий тип события для обработки различных типов данных о событиях. Вот пример обработки событий Spring с использованием дженериков:
Язык кода:java
копировать
public class GenericEvent<T> extends ApplicationEvent {

    private T eventData;

    public GenericEvent(Object source, T eventData) {
        super(source);
        this.eventData = eventData;
    }

    public T getEventData() {
        return eventData;
    }
}

@Service
public class EventPublisherService {

    private final ApplicationEventPublisher eventPublisher;

    public EventPublisherService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public <T> void publishGenericEvent(T eventData) {
        GenericEvent<T> genericEvent = new GenericEvent<>(this, eventData);
        eventPublisher.publishEvent(genericEvent);
    }
}

@Component
public class GenericEventListener {

    @EventListener
    public <T> void handleGenericEvent(GenericEvent<T> event) {
        T eventData = event.getEventData();
        System.out.println("Received a generic event with data: " + eventData);
    }
}

Обработка исключений прослушивателя

  • Мы выбираем разные способы обработки исключений в зависимости от того, как выполняется прослушиватель.
  • Не рекомендуется использовать его в реальном бизнесе. Смысл самого Spring Event в том, чтобы отделить внутренние компоненты, и каждый слушатель должен быть максимально независимым.

Синхронная обработка исключений

  • Настройте ErrorHandler и привяжите его к SimpleApplicationEventMulticaster.
Язык кода:java
копировать
@Component
public class MyErrorHandler implements ErrorHandler {
    @Override
    public void handleError(Throwable t) {
        log.info("handle error -> {}", t.getClass());
    }
}

@Service
public class EventListenerService {

    @Autowired
    private SimpleApplicationEventMulticaster simpleApplicationEventMulticaster;

    @Autowired
    private MyErrorHandler errorHandler;

    @PostConstruct
    public void init(){
        simpleApplicationEventMulticaster.setErrorHandler(errorHandler);
    }
}

асинхронный Обработка исключений прослушивателя

  • Используйте SimpleAsyncUncaughtExceptionHandler для обработки исключений, создаваемых @Async.
Язык кода:java
копировать
@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

лучшие практики

Слушатели по умолчанию выполняются синхронно

  • Все прослушиватели событий выполняются синхронно. Необходимо оценить влияние синхронной блокировки на текущий основной процесс. Рекомендуется использовать асинхронный метод.

Обработка событий прослушивателя ненадежна

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

Сохраняйте логику слушателя как можно меньшей

  • Логика прослушивателя событий должна быть сведена к минимуму, она должна действовать просто как связующее звено между различными частями программы; любая существенная логика должна быть помещена в конкретные реализации классов обслуживания;

Не полагайтесь на порядок выполнения прослушивателя

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

Используйте условные прослушиватели и прослушиватели транзакций с осторожностью.

  • Хотя оба инструмента являются мощными, их слишком частое использование может вызвать проблемы в наших программах, которые трудно отладить.

Профиль

👋 привет, я Lorin Лорейн, один Java Разработчик бэкэнд-технологий!девиз:Technology has the power to make the world a better place.

🚀 Моя страсть к технологиям — это моя мотивация продолжать учиться и делиться ими. Мой блог — это место об экосистеме Java, серверной разработке и последних технологических тенденциях.

🧠 как Java Будучи энтузиастом серверных технологий, я не только увлечен изучением новых возможностей языков и глубины технологий, но и страстно желаю поделиться своими идеями и знаниями. практика. Я верю, что обмен знаниями и сотрудничество с сообществом могут помочь нам расти вместе.

💡 В моем блоге вы найдете подробные статьи об основных концепциях Java, базовой технологии JVM, часто используемых платформах, таких как Spring и Mybatis, управлении базами данных, таких как MySQL, промежуточном программном обеспечении для обработки сообщений, таком как RabbitMQ и Rocketmq, оптимизации производительности и т. д. Я также поделюсь некоторыми советами по программированию и методами решения проблем, которые помогут вам лучше освоить программирование на Java.

🌐 Я поощряю взаимодействие и создание сообщества, поэтому, пожалуйста, оставляйте свои вопросы, предложения или запросы по темам и дайте мне знать, что вас интересует. Кроме того, я буду делиться последними новостями Интернета и технологий, чтобы вы всегда были в курсе последних событий в мире технологий. Я с нетерпением жду возможности вместе с вами двигаться вперед по пути технологий и исследовать безграничные возможности мира технологий.

📖 Следите за обновлениями моего блога и давайте вместе стремиться к техническому совершенству.

ЯсуществоватьучаствоватьНа третьем этапе специального тренировочного лагеря Tencent Technology Creation 2023 года будет проводиться конкурс сочинений. Соберите команду, чтобы выиграть приз!

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose