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 响应,如果耗时过长会影响用户体验,根据需要也可以将事件配置为异步方式。
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