当某个业务完成后,需要做另外一些操作。如果写在代码中,一个一个去请求,虽然也能完成,但是这样代码耦合,遇见新增操作也需要找到代码进行修改。
有一种更好的方式,那就是事件监听,事件监听也是设计模式中 发布-订阅模式、观察者模式的一种实现。
可以将业务做完后,发布一个事件,将必须的参数通过事件一同发布出去。发布后所有订阅该事件的监听都会被触发并拿到传递的参数,可以在每个监听中分别进行不同的操作。比如减库存、清购物车等等。
1.定义一个事件,用来发布、提供给监听器去监听。事件可以简单理解为,对中间参数的传递介质来使用。业务完成后,将各个监听器需要的参数通过有参构造方法进行依赖注入,将DTO传入事件内部,监听器监听到后再从接口中取到DTO实体,再进而取出来参数,完成参数传递。(事件中定义getter方法来供给外部获取DTO)。
public class MyUserEvent extends ApplicationEvent {
private UserInfo userInfo;
public UserInfo getUserInfo() {
return this.userInfo;
}
public MyUserEvent(UserInfo source) {
//必要代码行
super(source);
//依赖注入
this.userInfo = source;
//参数统一处理
userInfo.setNickName("事件中被创建");
System.out.println("===MyUserEvent被发布==");
//切面逻辑
System.out.println("====切面逻辑===");
}
}
2.创建监听器来监听事件
创建监听器有两种方法
① 实现ApplicationListener 接口
Object是需要监听的事件的实体,需要重写 onApplicationEvent 方法,监听到事件后就会触发 onApplicationEvent 接口
@Component public class MyListenerEventTwo implements ApplicationListener{ @Autowired UserInfoService userInfoService; @Override public void onApplicationEvent(MyUserEvent event) { UserInfo userInfo = event.getUserInfo(); userInfo.setId("2"); userInfo.setPhone("222222"); userInfo.setUserName("被监听事件二创建"); userInfoService.save(userInfo); } }
这种方法不经常使用,一般使用第二种方法
② 使用注解 @EventListener(classes = {Object.class})
使用注解更简便,并且同一Java文件内可以有多个监听事件。对于同一个事件的不同监听,可以使用 **@Order()**注解来指定执行顺序。
@Component
public class MyListenerEventTwo {
@Autowired
UserInfoService userInfoService;
@EventListener(classes = {MyUserEvent.class})
@Order(1)
public void onApplicationEventOne(MyUserEvent event) {
UserInfo userInfo = event.getUserInfo();
userInfo.setId("1");
userInfo.setPhone("11111");
userInfo.setUserName("被监听事件一创建");
userInfoService.save(userInfo);
}
@EventListener(classes = {MyUserEvent.class})
@Order(2)
public void onApplicationEventTwo(MyUserEvent event) {
UserInfo userInfo = event.getUserInfo();
userInfo.setId("2");
userInfo.setPhone("222222");
userInfo.setUserName("被监听事件二创建");
userInfoService.save(userInfo);
}
}
3.事件发布(事件广播),监听器触发
事件需要被spring容器发布后,才会被事件给监听到并执行监听器内部逻辑。
现在需要发布的位置注入依赖
@Autowired
private ApplicationContext applicationContext;
然后将事件交由依赖来发布
@GetMapping("/test12")
@Transactional(rollbackFor = Exception.class)
public void test() {
System.out.println("发布事件");
//通过ApplicationContext 发布事件
//事件因为有有参构造,因此可以注入参数
applicationContext.publishEvent(new MyUserEvent(new UserInfo()));
}
4.监听器监听到,触发执行接口
注意: 这里面的@Order()注解是spring里面的注解,不要引入错误
事务相关到这里,事件发布、监听触发就完成了。但是当前发布事件的模块与各个监听模块都位于同一个事务中,因此主发布模块或者监听模块发生异常报错,都是会同步回滚的。
在监听方法上面加上 @Async 异步注解,即可脱离事务管理(启动类需要加上@EnableAsync注解才可以生效异步)。加之前还是相当于同一个线程,不管主业务还是监听,只要一方发生报错,都会回滚。加之后就相当于事务分离开了。
因为指定执行顺序是在一个线程中指定的,因此order注解与Async注解不能同时使用
@Component
public class MyListenerEventTwo {
@Autowired
UserInfoService userInfoService;
@EventListener(classes = {MyUserEvent.class})
@Async
public void onApplicationEventOne(MyUserEvent event) {
System.out.println("监听器一启动");
UserInfo userInfo = event.getUserInfo();
userInfo.setId("1");
userInfo.setPhone("11111");
userInfo.setUserName("被监听事件一创建");
userInfoService.save(userInfo);
}
@EventListener(classes = {MyUserEvent.class})
@Async
public void onApplicationEventTwo(MyUserEvent event) {
System.out.println("监听器二启动");
UserInfo userInfo = event.getUserInfo();
userInfo.setId("2");
userInfo.setPhone("222222");
userInfo.setUserName("被监听事件二创建");
userInfoService.save(userInfo);
}
}
完结



