zoukankan      html  css  js  c++  java
  • Spring中的事件

    Spring框架中的事件

    spring中的事件通过观察者模式实现的。在spring容器中通过ApplicationEvent类和ApplicationListener接口处理事件,如果某个bean实现了ApplicationListener接口并部署到容器中,那么每次对应的ApplicationEvent被发布到容器中都会通知该bean。

    spring事件默认是同步的,即调用publishEvent方法发布事件后,会处于阻塞状态,直到onApplicationEvent接收到事件并处理返回之后才继续执行下去。

    ApplicationEvent

    表示事件本身,自定义事件需要继承该类,可以用来传递数据,比如上述操作,我们需要将用户的邮箱地址传给事件监听器.

    ApplicationListener

    事件监听器接口,事件的业务逻辑封装在监听器里面.

    ApplicationEventPublisherAware

    事件发送器,通过实现这个接口,来触发事件.

    实例

    现在通过一个例子来说明一下。假如用户登录之后,需要发送邮件、推荐商品、插入用户信息

    定义事件:UserRegisterEvent

    定义监听器:NotifyUserListener(发邮件)、RecommendListener(推荐商品)、UserInsertListener(插入用户信息)

    1 @Data
    2 @ToString
    3 public class User {
    4     private String name;
    5     private String age;
    6     private String phone;
    7 }
     1 @ToString
     2 @Getter
     3 @Setter
     4 public class UserRegisterEvent extends ApplicationEvent {
     5     /**
     6      * Create a new ApplicationEvent.
     7      *
     8      * @param source the object on which the event initially occurred (never {@code null})
     9      */
    10     public UserRegisterEvent(Object source) {
    11         super(source);
    12     }
    13     public void sayHello(){
    14         System.out.println("hello spring event");
    15     }
    16     private User user;
    17 }
    1 @Component
    2 public class NotifyUserListener implements ApplicationListener<UserRegisterEvent> {
    3     @Override
    4     public void onApplicationEvent(UserRegisterEvent event) {
    5         System.out.println("NotifyUserListener:onApplicationEvent()");
    6         UserRegisterEvent userRegisterEvent = event;
    7         System.out.println("发送邮件...");
    8     }
    9 }
    1 @Component
    2 public class RecommendListener implements ApplicationListener<UserRegisterEvent> {
    3     @Override
    4     public void onApplicationEvent(UserRegisterEvent event) {
    5         System.out.println("RecommendListener:onApplicationEvent()");
    6         UserRegisterEvent userRegisterEvent = event;
    7         System.out.println("推荐商品...");
    8     }
    9 }
    1 @Component
    2 public class UserInsertListener implements ApplicationListener<UserRegisterEvent> {
    3     @Override
    4     public void onApplicationEvent(UserRegisterEvent event) {
    5         System.out.println("UserInsertListener:onApplicationEvent()");
    6         UserRegisterEvent userRegisterEvent = event;
    7         System.out.println("插入用户信息,用户名..." + userRegisterEvent.getUser().getName());
    8     }
    9 }
     1 @Component
     2 public class UserRegisterPublisherService implements ApplicationEventPublisherAware {
     3     private ApplicationEventPublisher applicationEventPublisher;
     4 
     5     @Override
     6     public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
     7         this.applicationEventPublisher = applicationEventPublisher;
     8     }
     9 
    10     public void insert(User user) {
    11         UserRegisterEvent event = new UserRegisterEvent(JSON.toJSONString(user));
    12         applicationEventPublisher.publishEvent(event);
    13     }
    14 
    15     public void publish() {
    16         UserRegisterEvent ce = new UserRegisterEvent(this);
    17         User user = new User();
    18         user.setName("YO");
    19         user.setAge("1000");
    20         ce.setUser(user);
    21         System.out.println("UserRegisterPublisherService: publish");
    22         applicationEventPublisher.publishEvent(ce);
    23     }
    24 }
    1 /**
    2  * @author gyh
    3  * @description //自动扫描包名下所有使用@Component、@Service、@Repository和@Controller的类,并注册为Bean
    4  * @create 2020/10/28
    5  */
    6 @Configuration
    7 @ComponentScan("com.event")
    8 public class EventConfig {
    9 }
     1 public class MainApp {
     2     public static void main(String[] args) {
     3         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
     4         UserRegisterPublisherService registerPublisherService = context.getBean(UserRegisterPublisherService.class);
     5         System.out.println("start");
     6         System.out.println("用户已登录...");
     7         registerPublisherService.publish();
     8         context.close();
     9         System.out.println("end");
    10     }
    11 }
    start
    用户已登录...
    发送邮件...
    RecommendListener:onApplicationEvent()
    推荐商品...
    UserInsertListener:onApplicationEvent()
    插入用户信息,用户名...YO
    end

    SmartApplicationListener实现有序的监听

    SmartApplicationListener接口继承了ApplicationListener接口,使用全局的ApplicationEvent作为监听的对象。

    像比于ApplicationListener提供了排序功能、校验是否是我们监听的事件,是通过两个方法#supportsEventType、#supportsSourceType 来判定,只有这两个方法同时返回true时才会执行onApplicationEvent方法。

     1 package com.smartEvent;
     2 
     3 import org.springframework.context.ApplicationEvent;
     4 import org.springframework.context.event.SmartApplicationListener;
     5 import org.springframework.stereotype.Component;
     6 
     7 @Component
     8 public class NotifyUserListener implements SmartApplicationListener {
     9     @Override
    10     public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    11         return eventType == UserRegisterEvent.class;
    12     }
    13 
    14     @Override
    15     public boolean supportsSourceType(Class<?> sourceType) {
    16         return true;
    17     }
    18 
    19     @Override
    20     public void onApplicationEvent(ApplicationEvent event) {
    21         System.out.println("NotifyUserListener:onApplicationEvent()");
    22         UserRegisterEvent userRegisterEvent = (UserRegisterEvent) event;
    23         System.out.println("发送邮件...");
    24     }
    25 
    26     @Override
    27     public int getOrder() {
    28         return 2;
    29     }
    30 }

    还可以使用注解的方式实现

     1 package com.smartEvent;
     2 
     3 import org.springframework.context.event.EventListener;
     4 import org.springframework.core.annotation.Order;
     5 import org.springframework.stereotype.Component;
     6 
     7 @Component
     8 public class UserInsertListener {
     9     @Order(1)
    10     @EventListener(classes = com.smartEvent.UserRegisterEvent.class)
    11     public void onApplicationEvent(UserRegisterEvent event) {
    12         System.out.println("UserInsertListener:onApplicationEvent()");
    13         System.out.println("插入用户信息,用户名..." + event.getUser().getName());
    14     }
    15 }

    异步事件

    spring事件默认是同步的,同步事件会使得ApplicationEventPublisher 发布事件之后一直等待listener 响应,如果耗时过长会影响用户体验,根据需要也可以将事件配置为异步方式。

    可以使用Spring 提供的线程池注解 @Async 来实现异步线程。使用这个注解实现异步线程有个前提, 要在启动类商添加@EnableAsync 注解。如果不先使用系统提供的线程池配置,也可以通过下面的方法手动指定

     1 package com.asyncEvent;
     2 
     3 import com.google.common.util.concurrent.ThreadFactoryBuilder;
     4 import org.springframework.context.annotation.Bean;
     5 import org.springframework.context.annotation.Configuration;
     6 import org.springframework.scheduling.annotation.EnableAsync;
     7 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
     8 
     9 import java.util.concurrent.*;
    10 
    11 
    12 @Configuration
    13 @EnableAsync
    14 public class ThreadPoolConfig {
    15 
    16     /**
    17      * 线程池【方式1】
    18      */
    19     @Bean(value = "executeThreadPool")
    20     public Executor executeSettlementThreadPool() {
    21 
    22         ThreadFactory threadFactory = new ThreadFactoryBuilder()
    23                 .setNameFormat("execute-settlement-thread-%d").build();
    24 
    25         ExecutorService pool = new ThreadPoolExecutor(5, 10, 30L, TimeUnit.MILLISECONDS,
    26                 new LinkedBlockingQueue<Runnable>(10000), threadFactory, new ThreadPoolExecutor.AbortPolicy());
    27 
    28         return pool;
    29     }
    30     /**
    31      * 线程池【方式二】
    32      */
    33     @Bean(value = "executeThreadPoolV2")
    34     public Executor executeSettlementThreadPoolV2() {
    35 
    36         ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    37                 .setNameFormat("consumer-queue-thread-%d").build();
    38         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    39         // 线程池维护线程的最少数量
    40         executor.setCorePoolSize(5);
    41         // 线程池维护线程的最大数量
    42         executor.setMaxPoolSize(10);
    43         // 缓存队列
    44         executor.setQueueCapacity(25);
    45         //线程名
    46         executor.setThreadFactory(namedThreadFactory);
    47         // 线程池初始化
    48         executor.initialize();
    49         return executor;
    50     }
    51 }

    在每个listener中打印当前线程id,并将插入用户信息的listener改为异步方式

    package com.asyncEvent;
    
    import org.springframework.context.event.EventListener;
    import org.springframework.core.annotation.Order;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    @Component
    public class UserInsertListener {
        @Async("executeThreadPool")
        @Order(1)
        @EventListener(classes = UserRegisterEvent.class)
        public void onApplicationEvent(UserRegisterEvent event) {
            System.out.println("UserInsertListener:onApplicationEvent()");
            System.out.println("current thread id:"+Thread.currentThread().getId());
            System.out.println("插入用户信息,用户名..." + event.getUser().getName());
        }
    }

    运行结果:

     1 start
     2 用户已登录...
     3 UserRegisterPublisherService: publish
     4 NotifyUserListener:onApplicationEvent()
     5 current thread id:1
     6 发送邮件...
     7 RecommendListener:onApplicationEvent()
     8 current thread id:1
     9 推荐商品...
    10 current thread id:1 
    11 end
    12 UserInsertListener:onApplicationEvent()
    13 current thread id:11
    14 插入用户信息,用户名...YO
    15 
    16 Process finished with exit code 0

    可以看到插入用户信息的listener的线程id变成了11,其余都是1,就实现了异步方式的事件监听。

    参考博客

    https://www.cnblogs.com/rickiyang/p/12001524.html 作者:rickiyang

  • 相关阅读:
    shell脚本进阶
    sort与uniq命令
    sed命令
    DNS与CDN
    nginx
    Docker Private Registry
    docker存储卷
    docker容器网络配置
    docker容器网络
    docker容器虚拟化
  • 原文地址:https://www.cnblogs.com/LifeFruit/p/13884783.html
Copyright © 2011-2022 走看看