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

原文地址:https://www.cnblogs.com/LifeFruit/p/13884783.html