Spring中的事件监听模式

首先来看看Spring中事件机制的模型:

image.png

可以看到,整个Spring的事件机制结构还是比较简单的,主要有以下几个角色:

ApplicationEvent:Spring中所有事件对象都继承自ApplicationEvent。Spring中提供了两种事件类型,第一种是Spring容器自己的事件,一种是用户自定义的事件。Spring中是根据事件类型来区分不同的事件处理。Spring中通过ApplicationEventPublisher接口来发布事件,类似前面PublicPriceService中的publishEvent方法:

public interface ApplicationEventPublisher { //发布一个事件对象,传入的就是创建好的事件对象; void publishEvent(ApplicationEvent event); //发布一个事件对象,Spring会将普通的event对象转变成一个 //PayloadApplicationEvent消息。这个方法更灵活,要求event //不一定必须继承ApplicationEvent,但是仍然不建议这个方法。 void publishEvent(Object event);}

Spring的最强大容器接口ApplicationContext实现了ApplicationEventPublisher接口,并在AbstractApplicationContext类中实现。代码如下,可以清楚的看到,最终调用了ApplicationEventMulticaster来发布消息。

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { @Override public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } @Override public void publishEvent(Object event) { publishEvent(event, null); } protected void publishEvent(Object event, ResolvableType eventType) { ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; }else { //处理PayloadApplicationEvent } if (this.earlyApplicationEvents != null) { }else { //得到容器中的ApplicationEventMulticaster,并调用multicastEvent发布消息getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // 处理容器继承结构中消息的向上传递 if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } } //其他代码}

ApplicationEventMulticaster把责任分离出来,专门用于负责事件监听器和事件发布相关处理,我们来看看该接口一些重要方法:

public interface ApplicationEventMulticaster { /** * 添加一个监听器 */ void addApplicationListener(ApplicationListener> listener); /** * 删除一个监听器 */ void removeApplicationListener(ApplicationListener> listener); /** * 删除所有监听器 */ void removeAllListeners(); /** * 发布一个消息到所有对应的监听器 */ void multicastEvent(ApplicationEvent event, ResolvableType eventType);}

Spring中事件监听器的使用

最后,我们来看看Spring中事件监听器的使用。这里,我们就使用Spring来完成之前的示例。Spring中使用事件监听,有两种使用方式:

通过实现ApplicationListener,并配置完成;通过@EventListener标签完成;

首先我们来创建对应的价格变动事件对象:

@Getterpublic class PriceChangeEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; private BigDecimal price; public PriceChangeEvent(Object source) { super(source); } public PriceChangeEvent(Object source, BigDecimal price) { super(source); this.price = price; }}

然后我们定义价格变动业务类:

@Servicepublic class PublicPriceService { @Autowired private ApplicationEventPublisher publisher; public void priceChange() { BigDecimal price = getPrice(); publisher.publishEvent(new PriceChangeEvent(this, price)); } /** * 获取价格 * * @return */ private BigDecimal getPrice() { Double decimal = new Random().nextDouble(); int integer = new Random().nextInt(1000); return new BigDecimal(Math.abs(integer) + decimal).setScale(2, RoundingMode.HALF_UP); }}

到这里,两种方式都是相同的,主要的区别还是在监听器的实现上。我们先看第一种,实现ApplicationListener的方式:

/** * 邮件发送服务 * * @author stef * */@Componentpublic class MailSender implements ApplicationListener { @Override public void onApplicationEvent(PriceChangeEvent event) { System.out.println("发送邮件,价格改变为:" + event.getPrice()); }}

注意,需要添加@Component标签,把监听器注册到Spring容器中;

@Componentpublic class PriceLogger implements ApplicationListener { @Override public void onApplicationEvent(PriceChangeEvent event) { System.out.println("记录到日志文件:" + event.getPrice()); }}

最后,来完成测试:

@RunWith(SpringJUnit4ClassRunner.class)@Configuration@ComponentScan@ContextConfiguration(classes = PublicPriceServiceTest.class)public class PublicPriceServiceTest { @Autowired private PublicPriceService priceService; @Test public void testPrice() { priceService.priceChange(); }}

我们测试代码直接使用Spring的Javaconfig+springtest完成的。

好,接下来我们看看使用@EventListener来完成的例子。PriceChangeEvent,PublicPriceService和我们的测试代码都是相同的,只是需要修改一下两个监听器的代码:

@Componentpublic class PriceListener { /** * 发送邮件 * * @param event */ @EventListener public void onPriceChangeEventMail(PriceChangeEvent event) { System.out.println("发送邮件,价格改变为:" + event.getPrice()); } /** * 价格变动日志记录服务 * * @param event */ @EventListener public void onPriceChangeEventLog(PriceChangeEvent event) { System.out.println("记录到日志文件:" + event.getPrice()); }}

注意几个点:

需要作为事件监听器处理的逻辑直接写到方法里面,方法上面打上@EventListener标签即可;方法需要传入一个ApplicationEvent类型,相当于声明了一个ApplicationListener的类;使用@EventListener的好处在于,可以极大的减少监听器类的数量,非常方便管理。在一个类中,可以把大部分的监听器逻辑都完成。但是问题在于插拔不太方便,而且这个类也容易出现大量代码。可以在方法上同时配合@Async标签,将事件监听器直接变为异步消息处理。当然,同时需要在PriceListener上添加@EnableAsync注解。

选择

如果项目依赖Spring,那么遇到类似的情况,我建议直接使用Spring的事件机制来完成即可。如果仅仅是一个很小的项目,其中一个地方需要使用到观察者模式,建议可以使用Java提供的Observer和Observable;如果觉得Java提供的观察者模式在性能上,或者实现上不能满足要求(比如提到过Java实现的观察者模式,可能出现刚添加的观察者无法接收到消息的情况),可以自己使用Java提供的事件机制来完成。

作者:叩丁狼教育stef

查看原文 >>
相关文章