觀察者模式在Spring中的主要體現在事件監聽,事件機制的實現需要三個部分,事件源,事件,事件監聽器;
1 ApplicationEvent抽象類作爲事件的父類,通過source獲取事件源。
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
2 ApplicationListener接口作爲事件監聽器,繼承自EventListener
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
3 ApplicationContext接口作爲事件源,在這裏可以將監聽器註冊進應用上下文中,也可以觸發指定的事件。在ApplicationContext的父類ApplicationEventPublisher中有這樣一個方法publishEvent,用以發佈事件,具體的實現類在AbstractApplicationContext中。
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
同樣在AbstractApplicationContext中有方法registerListeners,可以註冊監聽器。
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
下面模擬Spring的監聽器原理自己實現一個登陸登出事件的監聽機制。
1 事件類(ApplicationEvent)
@Data
public class Event implements Serializable {
//事件源,誰觸發的
private Object source;
//觸發類型的標記
private String trigger;
//通知的目標對象
private Object target;
//通知的目標的方法
private Method callback;
//觸發時間
private long time;
public Event() {
}
public Event(Object target, Method callback) {
this.target = target;
this.callback = callback;
}
}
2 事件監聽抽象類,或者接口(ApplicationListener)
/**
* 事件監聽器
*/
@Slf4j
public abstract class EventListener {
/**
* 存放事件
*/
private List<Event> eventList = new ArrayList<>();
/**
* 添加事件監聽
*/
public void addEvent(Object target, Method method) {
Event event = new Event(target, method);
eventList.add(event);
}
/**
* 刪除事件監聽
*/
public void removeEvent(Event e) {
eventList.remove(e);
}
/**
* 通過事件觸發
*/
public void trigger(Event event) {
event.setSource(this);
event.setTime(System.currentTimeMillis());
try {
//調用指定的方法
event.getCallback().invoke(event.getTarget(), event);
} catch (Exception e) {
log.info(e.getMessage());
}
}
/**
* 通過事件 類型出發,存儲事件的集合可以使用Map,自定義類型屬性
*/
}
3 具體事件,繼承自事件監聽抽象類(ApplicationContext)
/**
* 實例對象,具體的被觀察者,即被監聽的事件實現類
*/
public class ConcreteSubject extends EventListener {
/**
* 模擬登陸推出,異常事件監聽
*/
public void login(Event loginEvent) {
System.out.println("登陸事件");
super.trigger(loginEvent);
}
public void logout(Event logoutEvent) {
System.out.println("登出事件");
super.trigger(logoutEvent);
}
public void exception(Event exceptionEvent) {
System.out.println("異常事件");
super.trigger(exceptionEvent);
}
}
4 對於監聽到指定事件給出相應的反饋類
/**
* 觀察者方法,模擬觀察者觀察到指定事件後相應的動作
*/
public class LogEventCallback {
public void login(Event event) {
System.out.println("=======用戶登陸======" + event);
}
public void logout(Event event) {
System.out.println("=======用戶登出======" + event);
}
public void exception(Event event) {
System.out.println("=======異常======" + event);
}
}
5 測試類,測試登陸事件的監聽機制
public class LogEventTest {
public static void main(String[] args) throws NoSuchMethodException {
LogEventCallback target = new LogEventCallback();
//通過反射獲取指定的方法
Method login = target.getClass().getDeclaredMethod("login", Event.class);
Method logout = target.getClass().getDeclaredMethod("logout", Event.class);
Method exception = target.getClass().getDeclaredMethod("exception", Event.class);
ConcreteSubject object = new ConcreteSubject();
//將監聽事件放進監聽類中,與Spring中的ApplicationContext功能相同
object.addEvent(target, logout);
object.addEvent(target, login);
object.addEvent(target, exception);
object.trigger(new Event(target, login));
}
}