深入理解Spring事件监听机制

深入理解Spring事件监听机制

在之前的文章中,我分享过一篇关于观察者模式的文章,如有有兴趣的可以去看一下,对理解Spring事件监听机制有些帮助。

言归正传,下面我们进行Spring事件监听机制的分享

1: Spring事件监听

​ Spring 的事件监听机制是在 JDK 事件监听的基础上进行的扩展,也是在典型观察者模式上的进一步抽象和改进。Spring 中提供了一套默认的事件监听机制,在容器初始化时便使用了这套机制。并且 Spring 也提供了事件监听机制的接口扩展能力,开发者基于此可快速实现自定义的事件监听功能

2: Spring事件监听的核心原理

Spring 的监听机制是非常优秀的思想,它能够很好地实现代码解耦,将业务逻辑与非业务逻辑分离,让程序变得更加灵活和可维护。

首先我们要理解几个概念

事件源: 事件的触发者,也即是事件发布者

事件: 描述发生了什么事情的对象,也就是我们事件的信息

事件监听器: 监听到事件的发生的时候,做一些处理

实现Spring事件机制主要有4个接口:

1: 事件源 ApplicationEventPublisher

作用: 用于发布事件

@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
    
    //发布事件
	void publishEvent(Object event);

}
2: 事件监听器 ApplicationListener

作用: 监听对应的事件,做逻辑处理

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	//监听事件
	void onApplicationEvent(E event);


	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
		return event -> consumer.accept(event.getPayload());
	}

}
3: 事件 ApplicationEvent

作用: 构造事件模型

public abstract class ApplicationEvent extends EventObject {

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 7099057708183571937L;

	/** System time when the event happened. */
	private final long timestamp;


	//构造事件对象,带时间
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	//构造事件对象,定制时间区
	public ApplicationEvent(Object source, Clock clock) {
		super(source);
		this.timestamp = clock.millis();
	}


    //当前事件的时间
	public final long getTimestamp() {
		return this.timestamp;
	}

}

4: 事件监听器管理 ApplicationEventMulticaster

作用: 负责事件的发布和监听

public interface ApplicationEventMulticaster {
    //用于向事件广播器注册一个监听器。在广播器发送事件时,这个监听器将会接收到事件。
    void addApplicationListener(ApplicationListener<?> listener);
    
    //用于将一个已经注册的监听器从事件广播器中移除。
    void removeApplicationListener(ApplicationListener<?> listener);
    
    //用于移除所有注册的监听器。
    void removeAllListeners();
    
    //用于向所有已注册的监听器广播一个事件。
    void multicastEvent(ApplicationEvent event);
}

3: 代码示例

了解上面的原理,我们看下在应用中是怎么使用的。

1: 创建监听类,将类注入到Spring容器中

@Component
public class OrderEventListener {
    
    //这里使用了@Async注解异步线程执行,起作用的前提是开启@EnableAsync注解
    @EventListener
    @Async
    public void handleOrderEvent(OrderEvent event) {
        Order order = event.getOrder();
        String message = "您有一个新的订单,订单号为:" + order.getOrderId();
        System.out.println("message = " + message);
    }
}

2: 创建事件模型,用于事件信息传输

public class OrderEvent extends ApplicationEvent {
    public OrderEvent(Object source) {
        super(source);
    }

    private Order order;

    public OrderEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

3: 事件发布,将构造的事件发布出去

@RestController
@RequestMapping("/spring/event")
public class MyController {
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/order")
    public void pushEvent() {
        Order order = new Order();
        order.setOrderId("1111111111111111111");
        eventPublisher.publishEvent(new OrderEvent(this,order));
    }
}

以上就是Spring 事件监听的简单使用,是不是非常的简单方便。

4: 源码理解

在看源码之前,我们先思考几个问题

  1. 监听器是如何注入的?
  2. Spring是怎么管理监听器和事件的对应的?

OK,那我们带着问题进入Spring源码中解读

1: EventListenerMethodProcessor

通过@EventListener注解的搜索发现,该类中的processBean()方法对该注解进行处理

private void processBean(final String beanName, final Class<?> targetType) {
		if (!this.nonAnnotatedClasses.contains(targetType) &&
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				!isSpringContainerClass(targetType)) {

			Map<Method, EventListener> annotatedMethods = null;
			try {
				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 = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				for (Method method : annotatedMethods.keySet()) {
					for (EventListenerFactory factory : factories) {
						if (factory.supportsMethod(method)) {
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
                            //将对应的监听器放入到Spring的applicationEventMulticaster中
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
							beanName + "': " + annotatedMethods);
				}
			}
		}
	}
	
	public void addApplicationListener(ApplicationListener<?> listener) {
		Assert.notNull(listener, "ApplicationListener must not be null");
		if (this.applicationEventMulticaster != null) {
			this.applicationEventMulticaster.addApplicationListener(listener);
		}
		this.applicationListeners.add(listener);
	}

2: ApplicationEventPublisher.publishEvent()

	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			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 {
		   //拿到所有的监听器,对事件进行处理
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
        // 所有的监听器循环,匹配对应的事件
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
                //调用事件监听器
				invokeListener(listener, event);
			}
		}
	}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
		    //监听器分发事件, onApplicationEvent 就是我们自定义监听器的消费事件的地方
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent &&
							matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

以上就是Spring事件监听的源码。

5: 优缺点

  1. 松耦合:通过事件监听机制,组件之间的耦合度得到了很大的降低,每个组件只需要监听自己感兴趣的事件,不需要关心其他组件的实现细节。

  2. 可扩展性:通过定义新的事件类型和监听器,应用程序的功能可以随着需求的变化而不断扩展。

  3. 高可靠性:事件监听机制可以保证应用程序的可靠性,即使某个组件出现异常或错误,其他组件仍然可以正常运行。

6: 总结

​ 对于开发者来说,使用 Spring 监听机制非常简单。只需要实现事件和监听器接口,并在代码中注册监听器即可。Spring 会自动管理事件和监听器的生命周期,确保它们的正确运行。同时,由于 Spring 监听器使用了异步执行机制,因此不会影响主线程的运行效率,保证了应用程序的高并发和高效性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大伟攀高峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值