将一个流程做成低耦合可扩展性能:
1.注册一个用户person
2.注册之后给用户发送短信注册成功
3.注册之后给用户发送邮件最近活动信息
事件机制:
事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点
Spring 中事件机制中各角色:
- 事件 ApplicationEvent 是所有事件对象的父类,也就是说当某个业务发生改变 Spring 可以发出一个事件出来(当然这边可能是具体的某一个事件,Spring 中常用的事件请看第二节介绍)。
- 事件监听 ApplicationListener,也就是观察者,继承自 JDK 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。
- 事件源 ApplicationContext,ApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。
- 事件管理 ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
事件:注册一个用户
监听器:发送短信、发送邮件
增加了一个Listener来解耦UserService和其他服务,即注册成功后,只需要通知相关的监听器,不需要关系它们如何处理。增删功能非常容易。这就是一个典型的事件处理模型/观察者,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。
具体示例:
1.定义事件
public class UserRegisterEvent extends ApplicationEvent {
private String userName;
//source事件源
public UserRegisterEvent(Object source, String userName) {
super(source);
this.userName = userName;
}
public String getUserName() {
return userName;
}
}
2.定义监听器(两种方式)
方式1)实现接口方式:
/**
* 事件监听器:监听广播,发送邮件
*/
@Component
public class UserRegisterEventListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
System.out.println(String.format("接口模式给用户[%S]发送邮件成功",userRegisterEvent.getUserName()));
}
}
方式2)注解方式:
/**
* 事件监听器:监听广播,发送邮件
*/
@Component
public class UserRegisterEventListener2 {
@EventListener(classes = {UserRegisterEvent.class})
public void sendMail(UserRegisterEvent userRegisterEvent){
System.out.println("注解1给用户"+userRegisterEvent.getUserName()+"发送邮件成功");
}
@EventListener(UserRegisterEvent.class)
public void sendCompon(UserRegisterEvent userRegisterEvent){
System.out.println(String.format("注解2给用户[%s]发送邮件成功",userRegisterEvent.getUserName()));
}
}
第三、第四种摘取往上,
第三种方式:有序监听器实现SmartApplicationListener接口:
package com.br.listener;
import com.br.bean.User;
import com.br.event.UserRegisterEvent;
import com.br.service.UserService;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
/**
* @author 10400
* @create 2018-02-27 15:03
* @deprecated 有序监听
*/
@Component
public class SmartRegisterListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
//只有UserRegisterEvent监听类型才会执行下面逻辑
return aClass == UserRegisterEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
//只有在UserService内发布的UserRegisterEvent事件时才会执行下面逻辑
return aClass == UserService.class;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
//转换事件类型
UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
//获取注册用户对象信息
User user = userRegisterEvent.getUser();
//.../完成注册业务逻辑
System.out.println("SmartRegisterListener" + user.getName());
}
/**
* return 的数值越小证明优先级越高,执行顺序越靠前。
* @return
*/
@Override
public int getOrder() {
return 10;
}
}
SmartApplicationListener接口继承了全局监听ApplicationListener,
并且泛型对象使用的ApplicationEvent来作为全局监听,
可以理解为使用SmartApplicationListener作为监听父接口的实现,监听所有事件发布。
getOrder:return的数值越小证明优先级越高,执行顺序越靠前
第四种方式:使用@Async实现异步监听
@Aysnc其实是Spring内的一个组件,可以完成对类内单个或者多个方法实现异步调用,这样可以大大的节省等待耗时。内部实现机制是线程池任务ThreadPoolTaskExecutor,通过线程池来对配置@Async的方法或者类做出执行动作。
- 线程任务池配置
我们创建一个ListenerAsyncConfiguration,并且使用@EnableAsync注解开启支持异步处理,具体代码如下所示:
@Configuration
@EnableAsync
public class ListenerAsyncConfiguration implements AsyncConfigurer
{
/**
* 获取异步线程池执行对象
* @return
*/
@Override
public Executor getAsyncExecutor() {
//使用Spring内置线程池任务对象
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
我们自定义的监听异步配置类实现了AsyncConfigurer接口并且实现内getAsyncExecutor方法以提供线程任务池对象的获取。
我们只需要在异步方法上添加@Async注解就可以实现方法的异步调用,为了证明这一点,我们在发送邮件onApplicationEvent方法内添加线程阻塞3秒,修改后的代码如下所示:
/**
* supportsEventType & supportsSourceType 两个方法返回true时调用该方法执行业务逻辑
* @param applicationEvent 具体监听实例,这里是UserRegisterEvent
*/
@Override
@Async
public void onApplicationEvent(ApplicationEvent applicationEvent) {
try {
Thread.sleep(3000);//静静的沉睡3秒钟
}catch (Exception e)
{
e.printStackTrace();
}
//转换事件类型
UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
//获取注册用户对象信息
UserBean user = userRegisterEvent.getUser();
System.out.println("用户:"+user.getName()+",注册成功,发送邮件通知。");
}
网摘链接:https://www.jianshu.com/p/ef2cee8c5dd1
3.事件服务(任取一个事件管理实现接口即可ApplicationContextAware、ApplicationEventPublisherAware)
@Service
public class UserRegisterEventService implements ApplicationContextAware, ApplicationEventPublisherAware {
private static ApplicationContext applicationContext;
private static ApplicationEventPublisher applicationEventPublisher;
//注册
public void registerUser(String userName) {
UserRegisterEvent userRegisterEvent = new UserRegisterEvent(UserRegisterEvent.class, userName);
System.out.println(String.format("用户[%s]注册成功", userName));
//发布注册成功事件
applicationContext.publishEvent(userRegisterEvent);
applicationEventPublisher.publishEvent(userRegisterEvent);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@EventListener(classes = {UserRegisterEvent.class})
public void sendMail(UserRegisterEvent userRegisterEvent){
System.out.println("注解3给用户"+userRegisterEvent.getUserName()+"发送邮件成功");
}
@EventListener(UserRegisterEvent.class)
public void sendCompon(UserRegisterEvent userRegisterEvent){
System.out.println(String.format("注解4给用户[%s]发送邮件成功",userRegisterEvent.getUserName()));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
测试用例(结合上一篇测试用例规范注入Service服务):
/**
* 事件对象:用户注册事件
*/
@RunWith(SpringRunner.class)
//@SpringBootTest(classes = UserRegisterEventConfig.class)
@ContextConfiguration(classes = UserRegisterEventConfig.class)
public class UserRegisterTest {
@Resource
private UserRegisterEventService userRegisterEventService;
@Test
public void test() {
// ApplicationContext context = new AnnotationConfigApplicationContext(UserRegisterEventService.class);
// UserRegisterEventService userRegisterEventService = context.getBean(UserRegisterEventService.class);
System.out.println(userRegisterEventService);
userRegisterEventService.registerUser("userName222");
}
}