前言
你是否因为产品经理一个需求变更,要翻出布满灰尘的代码,一点点的进行修改,重新提测呢?
你是否因为代码的耦合度太高,改动一块代码,就不得不翻阅关联的所有代码,一步步关联修改?
这里推荐一个代码解耦的便捷方式,需求变更,业务逻辑调整,都不用修改核心代码就能完成需求——Spring中的发布订阅模式
实现方式一: 实现接口ApplicationListener
①事件对象
public class CommitExamEvent extends ApplicationEvent {
public CommitExamEvent(Object source) {
super(source);
}
}
②监听器
2.1 监听器接口
public interface CustomApplicationEventListener extends ApplicationListener {
/**
* 响应应用事件
* @param applicationEvent
*/
@Override
void onApplicationEvent(ApplicationEvent applicationEvent){
if(applicationEvent instanceof CommitExamEvent){
onCommitExamEvent((CommitExamEvent)applicationEvent);
}
}
void onCommitExamEvent(CommitExamEvent applicationEvent);
}
2.2 监听器实现类
加@Async注解可以实现异步调用,不加则是同步调用(线程ID相同)
@Service
@Slf4j
public class ServiceImpl implements CustomApplicationEventListener {
@Override
@Async
public void onCommitExamEvent(CommitExamEvent applicationEvent) {
//......
}
}
③发送事件
发送事件后,监听器就会调用onApplicationEvent方法
@Resource
private ApplicationContext applicationContext;
public void sendEvent(GroupInfo groupInfo) {
//发布分组信息被删除的事件,让订阅者进行响应
applicationContext.publishEvent(new CommitExamEvent(groupInfo));
}
实现方式二、@EventListener注解
①事件对象
与方式一相同
②监听方法(用@EventListener)
加@Async注解可以实现异步调用,不加则是同步调用(线程ID相同)
@EventListener
@Async
public void handleEvent(CommitExamEvent commitExamEvent) {
final PaperDto paperDto = (PaperDto) commitExamEvent.getSource();
final Long sysUserId = paperDto.getMainInfo().getSysUserId();
final Long trainingId = paperDto.getMainInfo().getTrainingId();
updateUserTrainingTotalProcess(sysUserId, trainingId);
}
③发送事件
与方式一相同
方式一与方式二两者对比:
相同点
两者都可以满足:当一个对象变更后需要关联的N个对象作出对应变更的需求,当需要新增或移除相关联的对象,不用改动之前的核心代码,只需增删监听器即可。
不同点
注解方式比较灵活,监听器不用实现接口(推荐);
实现接口的方式若有多个事件要处理,则必须写在同一个监听器接口中,若用多个监听器接口响应不同事件,在一个监听器实现类需要监听多种事件的情况,调用会报错,而使用注解方式就没有这种顾虑。