【小家Spring】从Spring中的事件(ApplicationEvent)驱动机制出发,聊聊【观察者模式】【发布订阅模式】【消息队列MQ】【EventSourcing】...

版权声明: https://blog.csdn.net/f641385712/article/details/88806639

每篇一句

人可以有追求,但切莫攀比。你虚荣可以,但一定要靠自己。父母给的是后盾,你自己打的才叫江山

相关阅读

【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)
【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(二),Spring容器启动/刷新的完整总结

前言

说到事件驱动,我心里一直就有一个不解的疑问:它和我们老生长谈的一些概念比如:【观察者模式】【发布订阅模式】【消息队列MQ】【消息驱动】【EventSourcing】等等是一回事吗?

可能很多小伙伴会回答:差不多。确实,很有深意的三字回答。

那么本文将以Spring的事件驱动机制为引子,好好的聊聊这里面的关系和差异~

JDK中的事件驱动机制

在了解其它之前,有必要先了解下JDK为我们提供的事件驱动(EventListener、EventObject)、观察者模式(Observer)。

JDK不仅提供了Observable类、Observer接口支持观察者模式,而且也提供了EventObjectEventListener接口来支持事件监听模式。

这些类都属于java.util下的

观察者模式(ObservableObserver) JDK1.0提供

被观察对象:观察者 = 1:n (观察者可以有N个嘛)

观察者(Observer)相当于事件监听者(监听器),被观察者(Observable)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数。简化了事件-监听模式的实现。

// 观察者,实现此接口即可
public interface Observer {
	// 当被观察的对象发生变化时候,这个方法会被调用
	//Observable o:被观察的对象
	// Object arg:传入的参数
    void update(Observable o, Object arg);
}

// 它是一个Class
public class Observable {

	// 是否变化,决定了后面是否调用update方法
    private boolean changed = false;
    // 用来存放所有`观察自己的对象`的引用,以便逐个调用update方法
    // 需要注意的是:1.8的jdk源码为Vector(线程安全的),有版本的源码是ArrayList的集合实现; 
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

	public synchronized void addObserver(Observer o); //添加一个观察者 注意调用的是addElement方法,添加到末尾   所以执行时是倒序执行的
	public synchronized void deleteObserver(Observer o);
	public synchronized void deleteObservers(); //删除所有的观察者

	// 循环调用所有的观察者的update方法
	public void notifyObservers();
	public void notifyObservers(Object arg);
    public synchronized int countObservers() {
        return obs.size();
    }

	// 修改changed的值
    protected synchronized void setChanged() {
        changed = true;
    }
    protected synchronized void clearChanged() {
        changed = false;
    }
    public synchronized boolean hasChanged() {
        return changed;
    }
}

它的使用非常的便捷,看个例子就能明白;

class Person extends Observable {
    public String name;

    public Person(String name) {
        this.name = name;
    }

    // 给鱼:这样所有观察的猫都会过来了
    // fishType: 鱼的名字
    public void giveFish(String fishName) {
        setChanged(); // 这个一定不能忘
        notifyObservers(fishName);
    }
}

class Cat implements Observer {
    public String name;

    public Cat(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        String preffix = o.toString();
        if (o instanceof Person) {
            preffix = ((Person) o).name;
        }
        System.out.println(preffix + "主人放 " + arg + "~了," + name + "去吃鱼吧");
    }
}

// 测试方法如下:
    public static void main(String[] args) {
        Person person = new Person("fsx");

        // 来10只猫 观察这个人
        for (int i = 0; i < 10; i++) {
            person.addObserver(new Cat("cat" + i));
        }

        //开始放fish,这时候观察的猫就应该都过来了
        person.giveFish("草鱼");
    }

// 输出
fsx主人放 草鱼~,cat9去吃鱼吧
fsx主人放 草鱼~,cat8去吃鱼吧
fsx主人放 草鱼~,cat7去吃鱼吧
fsx主人放 草鱼~,cat6去吃鱼吧
fsx主人放 草鱼~,cat5去吃鱼吧
fsx主人放 草鱼~,cat4去吃鱼吧
fsx主人放 草鱼~,cat3去吃鱼吧
fsx主人放 草鱼~,cat2去吃鱼吧
fsx主人放 草鱼~,cat1去吃鱼吧
fsx主人放 草鱼~,cat0去吃鱼吧

JDK的观察者模式使用起来确实非常的方便,我们只需要面对两个对象即可。内部观察者队列啥的都交给Observable去处理了。 并且,它是线程安全的

发布订阅模式(EventListenerEventObject) JDK1.1提供

Spring中的事件驱动机制

事件机制一般包括三个部分:EventObject,EventListener和Source
EventObject:事件状态对象的基类,它封装了事件源对象以及和事件相关的信息。所有java的事件类都需要继承该类
EventListener:是一个标记接口,就是说该接口内是没有任何方法的。所有事件监听器都需要实现该接口。事件监听器注册在事件源上,当事件源的属性或状态改变的时候,调用相应监听器内的回调方法(自己写)。
Source:一个普通的POJO。事件最初发生的地方,他里面必须含有监听它的监听器们

class MyEvent extends EventObject {
    public MyEvent(Object source) {
        super(source);
    }
}

// 状态改变事件
class StatusChangedListener implements EventListener {
    public void handleEvent(MyEvent event) {
        System.out.println(event.getSource() + " 的状态改变啦~");
    }

}

// 状态没变化事件
class StateSameListener implements EventListener {
    public void handleEvent(MyEvent event) {
        System.out.println(event.getSource() + " 的状态没有任何变化~");
    }
}

class MySource {
    private int status;
    List<EventListener> eventListeners = new ArrayList<>();

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void addListener(EventListener listener) {
        eventListeners.add(listener);
    }

    // 调用所有的合适的监听器
    public void notifyListeners(int oldStatus, int newStatus) {
        eventListeners.forEach(l -> {
            if (oldStatus == newStatus) {
                // doSamething
            } else {
                // doSamething
            }
        });
    }

}

// 测试方法
    public static void main(String[] args) {
        MySource mySource = new MySource();
        mySource.addListener(new StatusChangedListener());
        mySource.addListener(new StateSameListener());

        int oldStatus = mySource.getStatus();
        mySource.setStatus(1);
        int newStatus = mySource.getStatus();

        // 触发所有的监听者们
        mySource.notifyListeners(oldStatus, newStatus);
    }

对弈上面的观察者模式,监听模式使用起来确实非常的繁琐,且还线程安全问题还得自己考虑解决。我个人觉得JDK的源生的事件、监听模式非常难用(不太建议使用,它最大的败笔在于EventListener接口没有定义一个抽象方法,不知道是作何考虑的,应该是为了更加抽象吧)。因此接下来,大行其道的Spring事件机制就很好的解决使用上的问题~~~它也是今天的主菜

Spring中事件驱动机制

Spring提供了ApplicationEventPublisher接口作为事件发布者(ApplicationContext接口继承了该接口,担当着事件发布者的角色)。
Spring提供了ApplicationEventMulticaster接口,负责管理ApplicationListener和真正发布ApplicationEventApplicationContext是委托给它完成的)

ApplicationListener实现了JDK的EventListener,但它抽象出一个onApplicationEvent方法,使用更方便。ApplicationEvent继承自EventObject。 Spring这么做我觉得完全是为了兼容Java规范~

在博文:【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(二),Spring容器启动/刷新的完整总结
这里讲解IoC容器refresh()的时候,第八步:initApplicationEventMulticaster()和第十步:registerListeners()和第十二步:inishRefresh()方法里的publishEvent(new ContextRefreshedEvent(this))都是和时间机制相关的方法。

initApplicationEventMulticaster():我们向容器注册了一个SimpleApplicationEventMulticaster(若我们自己没指定的话),因此若我们希望手动控制时间的发布,是可以@Autowired进来的
registerListeners():会把所有的ApplicationListener添加进ApplicationEventMulticaster进行管理(注意此处并不包括@EventListener标注的注解方法)
publishEvent:发布事件。因为ApplicationContext继承了ApplicationEventMulticaster,因此我们一般发布时间建议用它就成了

public abstract class ApplicationEvent extends EventObject {
	private static final long serialVersionUID = 7099057708183571937L;	
	private final long timestamp;

	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	public final long getTimestamp() {
		return this.timestamp;
	}
}

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	// 此子接口提供了泛型,和提供了统一的处理方法
	void onApplicationEvent(E event);
}

@FunctionalInterface
public interface ApplicationEventPublisher {
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
	
	// 这个接口是Spring4.2后提供的,可以发布任意的事件对象(即使不是ApplicationEvent的子类了)
	// 当这个对象不是一个ApplicationEvent,我们会使用PayloadApplicationEvent来包装一下再发送
	// 比如后面会建讲到的@EventListener注解标注的放 就是使用的它
	void publishEvent(Object event);
}

我们知道Spring4.2后提供了@EventListener注解,让我们更便捷的使用监听了,非常非常非常的方便:

ApplicationListener实现类模式的演示和原理解析

这是Spring最早期就提供了的一种事件监听方式。实现起来也非常的简单。

通过Spring源码我们了解到,Spring容器刷新的时候会发布ContextRefreshedEvent事件,因此若我们需要监听此事件,直接写个监听类即可:

@Slf4j
@Component
public class ApplicationRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        Object source = event.getSource();
        // 此处的source就是ApplicationContext这个对象
        System.out.println(source); //WebApplicationContext for namespace 'dispatcher-servlet': startup date [Tue Mar 26 14:26:27 CST 2019]; parent: Root WebApplicationContext

        //容器此时已经准备好了,可以做你该做的事了~......(请注意:若存在父子容器或者多个容器情况,此方法会被执行多次,请务必注意是否幂等)

    }
}

若是web环境,FrameworkServlet在处理完每一次i请求,也会发出一个事件:ServletRequestHandledEvent

自己发布一个事件,然后自己监听~~~~

public class MyAppEvent extends ApplicationEvent {

    public MyAppEvent(Object source) {
        super(source);
    }
}

// 写个监听器,然后交给容器管理即可
@Slf4j
@Component
public class MyEventListener implements ApplicationListener<MyAppEvent> {

    @Override
    public void onApplicationEvent(MyAppEvent event) {
        Object source = event.getSource();
        long timestamp = event.getTimestamp();

        System.out.println(source);
        System.out.println(timestamp);
        //doSomething

    }
}

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // 发布自己的事件
        applicationContext.publishEvent(new MyAppEvent("this is my event"));
    }
// 输出:
this is my event
1553581974928

Spring内置的事件讲解

在这里插入图片描述
Web相关事件:

  • RequestHandledEvent:Web相关事件,只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件(即ServletRequestHandledEvent

ApplicationContextEvent:应用本身的事件

  • ContextRefreshedEvent:容器初始化完成刷新时触发。此时所有的Bean已经初始化完成、后置处理器等都已经完成
  • ContextStartedEventAbstractApplicationContext#strart()被调用时。 需要手动调用,个人觉得没啥卵用
  • ContextStoppedEvent:容器的stop方法被手动调用时。 也没啥卵用
  • ContextClosedEvent:close() 关闭容器时候发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
@EventListener注解方法模式演示和原理解析

在任意方法上标注@EventListener注解,指定 classes,即需要处理的事件类型,一般就是 ApplicationEven 及其子类(当然任意事件也是Ok的,比如下面的MyAppEvent就是个普通的POJO),可以设置多项。

public class MyAppEvent {

    private String name;

    public MyAppEvent(String name) {
        this.name = name;
    }
}

// 显然此处,它会收到两个时间,分别进行处理
@Component
public class MyAllEventListener {

    //value必须给值,但可以不用是ApplicationEvent的子类  任意事件都ok
    // 也可以给一个入参,代表事件的Event
    @EventListener(value = {ContextRefreshedEvent.class, MyAppEvent.class}
            // confition的使用,若同一个事件进行区分同步异步 等等条件的可以使用此confition 支持spel表达式  非常强大
            /*,condition = "#event.isAsync == false"*/)
    public void handle(Object o) {
        System.out.println(o);
        System.out.println("事件来了~");
    }
}

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        // 发布自己的事件
        applicationContext.publishEvent(new MyAppEvent("this is my event"));
    }

显然这种方式更被推崇,因为它是方法级别的,更轻便了。(Spring4.2之后提出)

Spring事件机制原理分析

事件收集(EventListenerMethodProcessor

事件的收集前面讲了继承ApplicationListener的收集情况,那么此处就重点说说Spring4.2后提供的关于@EventListener注解的情况,看看Spring是怎么收集到这些方法,然后管理起来的。

一切源于Spring容器启动过程中:AnnotationConfigUtils.registerAnnotationConfigProcessors(context)注册的7大基础组件时,其中有一个是EventListenerMethodProcessor,它就是处理EventListener注解然后把它注册为一个特别的ApplicationListener的处理器。 当然还有一个EventListenerFactory(DefaultEventListenerFactory)

// 它是一个SmartInitializingSingleton,所以他会在preInstantiateSingletons()的最后一步执行~~~
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {

	@Nullable
	private ConfigurableApplicationContext applicationContext;
	// 解析注解中的Conditon的
	private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator();
	// 视图 这样set也变成线程安全的了
	private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));	


	@Override
	public void afterSingletonsInstantiated() {
		// 从容器里获得所有的EventListenerFactory,它是用来后面处理标注了@EventListener方法的工厂(Spring默认放置的是DefaultEventListenerFactory,我们也可以继续放  支持@Order等注解)
		List<EventListenerFactory> factories = getEventListenerFactories();
		ConfigurableApplicationContext context = getApplicationContext();
		
		// 这里厉害了,用Object.class 是拿出容器里面所有的Bean定义~~~  一个一个的检查
		String[] beanNames = context.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
			// 不处理Scope作用域代理的类。 和@Scope类似相关
			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
				Class<?> type = null;
				try {
					// 防止是代理,吧真实的类型拿出来
					type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), beanName);
				}
				if (type != null) {
					// 对专门的作用域对象进行兼容~~~~(绝大部分都用不着)
					if (ScopedObject.class.isAssignableFrom(type)) {
						...
					}
					
					// 真正处理这个Bean里面的方法们。。。
					processBean(factories, beanName, type);
				}
			}
		}
	}


	protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {

		// 缓存下没有被注解过的Class,这样再次解析此Class就不用再处理了  
		//这是为了加速父子容器的情况  做的特别优化
		if (!this.nonAnnotatedClasses.contains(targetType)) {
			Map<Method, EventListener> annotatedMethods = null;
			try {
				// 这可以说是核心方法,就是找到这个Class里面被标注此注解的Methods们
				// 在讲述到反射专题的时候,相关方法会再具体分析
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			catch (Throwable ex) {
				// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
				}
			}

			// 若一个都没找到,就缓存起来,然后结束呗~~~~~~~~~~~~~~~~~~~~~
			if (CollectionUtils.isEmpty(annotatedMethods)) {
				this.nonAnnotatedClasses.add(targetType);
				if (logger.isTraceEnabled()) {
					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
				}
			}
			else {
				// Non-empty set of methods
				ConfigurableApplicationContext context = getApplicationContext();
				
				// 处理这些带有@EventListener注解的方法们
				for (Method method : annotatedMethods.keySet()) {
					for (EventListenerFactory factory : factories) {
						
						// 加工的工厂类也可能有多个,但默认只有Spring注册给我们的一个
						// supportsMethod表示是否支持去处理此方法(因为我们可以定义处理器,只处理指定的Method都是欧克的)  Spring默认实现永远返回true
						if (factory.supportsMethod(method)) {
							
							// 简单的说,就是把这个方法弄成一个可以执行的方法(主要和访问权限有关)
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));

							// 把这个方法包装成一个监听器ApplicationListener(ApplicationListenerMethodAdapter类型)
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {

								// 这个init方法是把ApplicationContext注入进去
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							
							// 添加进去  管理起来
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
			}
		}
	}
}

就着这样,最终我们所有的Listener都被管理了起来。

事件发布(SimpleApplicationEventMulticaster

我们一般都会使用AbstractApplicationContext#publish()来发布一个事件:

	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		// Decorate event as an ApplicationEvent if necessary
		// 如果这个事件不是ApplicationEvent类型,那就包装成这个类型
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		} else {
			// 注意此处:第一个参数为source,这里传的source,第二个是payload,才传的是事件本身
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			
			// 若没有指定类型。就交给PayloadApplicationEvent<T>,它会根据泛型类型生成出来的~~~
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		// 如果是早期事件,就添加进去  会立马发布了(一般都不属于这种)
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
			// 最终把这些时间都委派给了`ApplicationEventMulticaster` 让它去发送事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		// 此处注意:特别重要,如果是父容器,也会向父容器里广播一份~~~~~
		if (this.parent != null) {
			// 这个判断的用意是,既然eventType已经解析出来了,所以就调用protected内部方法即可,而不用再次解析一遍了
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			// 如果是普通的发布,就没有eventType了
			else {
				this.parent.publishEvent(event);
			}
		}
	}

因此重点看看ApplicationEventMulticaster#multicastEvent:它的唯一实现为:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	// 若set了一个执行器,那所有的监听器都将会异步执行
	@Nullable
	private Executor taskExecutor;
	// 监听者执行失败的回调~~~~~~(比如做回滚等等)
	@Nullable
	private ErrorHandler errorHandler;

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

		// 这里面有个细节:如果有执行器executor ,那就会扔给线程池异步去执行
		// 默认情况下是没有的(Spring默认情况下同步执行这些监听器的)  我们可以调用set方法配置一个执行器(建议)
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
}


	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 如果是实现了ApplicationListener接口,则直接调用其中的onApplicationEvent()方法;
			//如果是用@EventListener注释,则调用ApplicationListenerMethodAdapter中的onApplicationEvent()方法
			listener.onApplicationEvent(event);
		}
	}

ApplicationListenerMethodAdapter#onApplicationEvent

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		processEvent(event);
	}
	public void processEvent(ApplicationEvent event) {
		// 获取参数,最终会交给回调的方法的。事件类型是PayloadApplicationEvent,那就把.getPayload(),否则就是event本身喽
		Object[] args = resolveArguments(event);
		// 解析condition表达式(注意,此处把args传进去了) 因此我们表达式里是可以用这个参数的哦
		if (shouldHandle(event, args)) {
			
			// 就是执行目标方法,我们一般返回值都是void,所以就是null
			// 但是,但是,但是注意了,此处若返回的不是null,还有处理~~~~非常给力:
			Object result = doInvoke(args);
			if (result != null) {
				
				// 如果返回值是数组或者Collection,会把里面内容当作事件循环publishEvent
				// 如果就是个POJO,那就直接publish  
				// 事件的传递性 就这么的来了,强大啊
				handleResult(result);
			}
			else {
				logger.trace("No result object given - no result to handle");
			}
		}
	}

Spring的使用@EventListener监听事件。若监听方法有返回值,那将会把这个返回值当作事件源,一直发送下去,直到返回void或者null停止

    @EventListener(value = {ContextRefreshedEvent.class})
    public List<Child> handle(Object o) {
        List<Child> childList = new ArrayList<>();
        childList.add(new Child("1"));
        childList.add(new Child("2"));
        return childList;
    }

    // 因为上个方法有返回  所以事件会传递到此处
    @EventListener(Child.class)
    public void handChild(Child c) {
        System.out.println(c.getName() + " 发来了事件");
    }

输出:
1 发来了事件
2 发来了事件

Spring事件传递的应用场景,巧妙的使用,可以事半功倍。(当然必须了解原理,才能运用自如)

@EventListener使用中的小细节
  • @EventListener注解用在接口或者父类上都是没有任何问题的(这样子类就不用再写了,在接口层进行控制)
  • @EventListener标注的方法,无视访问权限
  • AbstractApplicationEventMulticaster的相关方法比如addApplicationListenerBean、removeApplicationListener。。。都是线程安全的。
  • 若想要异步执行事件,请自己配置@Bean这个Bean。然后setTaskExecutor()一个进去
@Component
public class MyAllEventListener implements MyAllEventListenerInterface {
    @Override
    public void handChild(Child c) {
        System.out.println(c.getName() + " 发来了事件");
    }
}

// 注解写在接口上,也是能正常work的~~~
interface MyAllEventListenerInterface {
    @EventListener(Child.class)
    void handChild(Child c);
}

各大模式大比拼

  • 观察者模式:它是设计模式里的一个术语。是一个非常经典的行为型设计模式。。猫叫了,主人醒了,老鼠跑了,这一经典的例子,是事件驱动模型在设计层面的体现。
  • 发布订阅模式:很多人认为等同于观察者模式。但我的理解是两者唯一区别,是发布订阅模式需要有一个调度中心,而观察者模式不需要(观察者的列表可以直接由被观察者维护)。 但它俩混用没问题,一般都不会在表达上有歧义
  • 消息队列MQ:中间件级别的消息队列(ActiveMQ,RabbitMQ),可以认为是发布订阅模式的一个具体体现

事件驱动->发布订阅->MQ,从抽象到具体。 因此MQ算是一个落地的产品了

  • EventSourcing:这个要关联到领域驱动设计。DDD对事件驱动也是非常地青睐,领域对象的状态完全是由事件驱动来控制。比如有著名的CQRS架构~~~

CQRS架构和微服务的关系:微服务的目的是为了从业务角度拆分(职责分离)当前业务领域的不同业务模块到不同的服务,每个微服务之间的数据完全独立,它们之间的交互可以通过SOA RPC调用(耦合比较高),也可以通过EDA 消息驱动(耦合比较低,比如我们常用的分布式产品:MQ)。

这类模式的优缺点

有点:

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象
  2. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用(保持职责单一,解耦)
  3. 观察者模式分离了观察者和被观察者二者的责任,这样让类之间各自维护自己的功能,专注于自己的功能,会提高系统的可维护性和可重用性。

缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃

总结

本文暂时只介绍了Spring中的一些简单的事件驱动机制,相信如果之后再看到Event,Publisher,EventListener·一类的单词后缀时,也能立刻和事件机制联系上了

知识交流

在这里插入图片描述
若群二维码失效,请加微信号(或者扫描下方二维码):fsx641385712。
并且备注:“java入群” 字样,会手动邀请入群

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/f641385712/article/details/88806639