这里写自定义目录标题
假设新需求
假如现在有一个新需求,当用户触发某一个动作的时候需要通知给不同业务模块去做处理,这个过程不能是同步的,你需要如何去做?
看到这个需求你是不是想到发布订阅观察者模式,而spring就是用了这种模式实现事件监听的,下面是我手写的代码,我们可以一起探讨一下,(#.#)
代码
管理监听器
首先我们需要有一个类来帮助我们管理我们的指定的监听器。再容器初始化的时候就要加载。
package com.judy.learn.core;
import cn.hutool.core.collection.CollectionUtil;
import com.judy.learn.event.ParentEvent;
import com.judy.learn.listener.ParentListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
/**
* 1 定义存放监听器容器
* 2 注册监听器
* 3 移除监听器
* 4 通知该事件所有监听器
*/
@Component
@Slf4j
public class EventManager implements ApplicationContextAware {
//定义存放监听器容器
private HashMap<Class<? extends ParentEvent>, List<ParentListener>> coreMap = new HashMap<>();
private static ApplicationContext applicationContext;
private static final String EVENT = "event";
/***
* 初始化监听器容器
*/
@PostConstruct
public void initListenerApplication() {
Map<String, ParentListener> beansOfType = applicationContext.getBeansOfType(ParentListener.class);
if (Objects.isNull(beansOfType)) {
log.info("获取监听器类 is null");
return;
}
beansOfType.forEach((key, value) -> {
Method[] declaredMethods = ReflectionUtils.getDeclaredMethods(value.getClass());
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals(EVENT)) {
Parameter parameter = declaredMethod.getParameters()[0];
//参数必须是event的子类
if (parameter.getName().equals(ParentEvent.class.getName())) {
continue;
}
//注册监听器
this.registerListener((Class<? extends ParentEvent>) parameter.getType(), value);
}
}
});
}
/**
* 注册监听器
*
* @param event 事件
* @param eventListener 监听器
*/
public void registerListener(Class<? extends ParentEvent> event, ParentListener eventListener) {
List<ParentListener> parentListeners = coreMap.get(event);
if (CollectionUtils.isEmpty(parentListeners)) {
parentListeners = new ArrayList<>();
}
parentListeners.add(eventListener);
coreMap.put(event, parentListeners);
}
/***
* 移除监听器
*/
public void removeListener(Class<? extends ParentEvent> event) {
coreMap.remove(event);
}
/**
* 通知监听器
*/
public void notifyListener(ParentEvent parentEvent) {
if (Objects.nonNull(coreMap)) {
List<ParentListener> parentListeners = coreMap.get(parentEvent.getClass());
if (CollectionUtil.isNotEmpty(parentListeners)) {
parentListeners.forEach(parentListener -> parentListener.event(parentEvent));
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
EventManager.applicationContext = applicationContext;
}
}
注解区分是否需要异步
当然我们也可以使用异步去加载我们的监听器,这样会提高我们的效率,也就是使用线程池来加载我们的监听器,我们可以通过注解的方式来区分是否需要使用异步加载
/***
* 通过注解实现异步注册监听器
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsyncExecuteListener {
}
异步加载监听器
package com.judy.learn.core;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.judy.learn.event.ParentEvent;
import com.judy.learn.listener.ParentListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/****
* 异步执行监听器
*/
@Component
@Slf4j
public class AsyncEventManager implements ApplicationContextAware {
public Map<Class<? extends ParentEvent>, List<ParentListener>> asyncMap = new HashMap<>();
private static ApplicationContext applicationContext;
private static final String EVENT = "event";
//线程池去通知监听器
private static ExecutorService eventPool = new ThreadPoolExecutor(4,
8, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(512), new ThreadFactoryBuilder().setNamePrefix("event-pool-%d").build());
/***
* 初始化监听器容器
*/
@PostConstruct
public void initListenerApplication() {
Map<String, ParentListener> beansOfType = applicationContext.getBeansOfType(ParentListener.class);
if (Objects.isNull(beansOfType)) {
log.info("获取监听器类 is null");
return;
}
beansOfType.forEach((key, value) -> {
Method[] declaredMethods = ReflectionUtils.getDeclaredMethods(value.getClass());
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals(EVENT)) {
Parameter parameter = declaredMethod.getParameters()[0];
//参数必须是event的子类
if (parameter.getName().equals(ParentEvent.class.getName())) {
continue;
}
//注册监听器
this.registerListener((Class<? extends ParentEvent>) parameter.getType(), value);
}
}
});
}
/***
* 注册监听器
* @param type
* @param value
*/
private void registerListener(Class<? extends ParentEvent> type, ParentListener value) {
List<ParentListener> parentListeners = asyncMap.get(type);
if (CollectionUtils.isEmpty(parentListeners)) {
parentListeners = new ArrayList<>();
}
parentListeners.add(value);
asyncMap.put(type, parentListeners);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
AsyncEventManager.applicationContext = applicationContext;
}
/***
* 通知事件监听器
* @param type
*/
public void notifyListener(ParentEvent type) {
List<ParentListener> parentListeners = asyncMap.get(type.getClass());
if (CollectionUtils.isEmpty(parentListeners)) {
return;
}
parentListeners.forEach(parentListener -> {
AsyncExecuteListener annotation = parentListener.getClass().getAnnotation(AsyncExecuteListener.class);
if (Objects.isNull(annotation)) {
//同步执行
parentListener.event(type);
} else {
//多线程异步执行
eventPool.execute(() -> parentListener.event(type));
}
}
);
}
}
发布事件通知监听器
/***
* 发布事件通知监听器
* @param <E>
*/
@Component
public class ParentPublisher<E extends ParentEvent> {
@Autowired
private EventManager eventManager;
public void publisher(E event) {
eventManager.notifyListener(event);
}
}
父监听器
所有的监听器都要集成ParentEvent
/***
* jdk要求所有事件必须都实现EventObject
*/
public class ParentEvent extends EventObject {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ParentEvent(Object source) {
super(source);
}
}
父listener,所有listener都要实现ParentListener
/***
* 事件监听器
* @param <E>
*/
public interface ParentListener<E extends ParentEvent> {
/***
* 事件
* @param e
*/
void event(E e);
}
代码实现例子
创建一个通知学生姓名和写别的事件
public class NoticeEvent extends ParentEvent {
@Getter
private Student student;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public NoticeEvent(Object source, Student student) {
super(source);
this.student = student;
}
}
创建一个NoticeEvent 的listener监听器
@Component
@Slf4j
public class NoticeListener implements ParentListener<NoticeEvent> {
@Override
public void event(NoticeEvent noticeEvent) {
log.info("通知学生:{},性别:{}", noticeEvent.getStudent().getName(), noticeEvent.getStudent().getSex());
}
}
@RequestMapping("/student")
@RestController
public class StudentController {
@Autowired
private ParentPublisher<NoticeEvent> noticeEventParentPublisher;
@GetMapping
public void studentNotice(String sex,String name) {
Student student = Student.builder().sex(sex).name(name).build();
NoticeEvent noticeEvent = new NoticeEvent(this, student);
noticeEventParentPublisher.publisher(noticeEvent);
}
}
总结
实现思路不止一种,思维方式不止一种!