基于Spring的发布订阅模式


基于Spring的发布订阅模式

在我们使用spring开发应用时,经常会碰到要去解耦合一些依赖调用,比如我们在做代码的发布流程中,需要去通知相关的测试,开发人员关注发布中的错误信息。而且通知这个操作又不希望强耦合在主业务流程中,这个时候我们很容易就想到了观察者设计模式,而spring恰好提供了事件-监听机制,让我们看一下他们是具体怎么实现的吧。

事件-监听机制:

  1. 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方);
  2. 当目标发送改变(发布),观察者(订阅者)就可以接收到改变;
  3. 观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;所以就松散耦合了它们之间的关系。

Spring提供的事件驱动模型/观察者抽象

img

其实整个模型就有三个角色,事件,目标(发布者),监听者,我们看一下spring中如何实现这三者

事件:

具体代表者是:ApplicationEvent:

1、 我们可以看到spring中ApplicationEvent该抽象类继承自JDK的EventObject。JDK要求所有事件将继承它,并通过source得到事件源。

package org.springframework.context;
 
import java.util.EventObject;
 
/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
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;
 
 
	/**
	 * Create a new ApplicationEvent.
	 * @param source the object on which the event initially occurred (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
 
 
	/**
	 * Return the system time in milliseconds when the event happened.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}
 
}
目标(发布者)

具体代表者是具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster。ApplicationContext该接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播):

ApplicationContext继承自ApplicationEventPublisher

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}

ApplicationEventPublisher定义了publishEvent方法

public interface ApplicationEventPublisher {
 
	/**
	 * Notify all matching listeners registered with this
	 * application of an application event. Events may be framework events
	 * (such as RequestHandledEvent) or application-specific events.
	 * @param event the event to publish
	 * @see org.springframework.web.context.support.RequestHandledEvent
	 */
	void publishEvent(ApplicationEvent event);
 
	/**
	 * Notify all matching listeners registered with this
	 * application of an event.
	 * 

If the specified {@code event} is not an {@link ApplicationEvent}, * it is wrapped in a {@link PayloadApplicationEvent}. * @param event the event to publish * @since 4.2 * @see PayloadApplicationEvent */ void publishEvent(Object event); }

在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播)

protected void publishEvent(Object event, ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
		}
 
		// 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);
			}
		}
	}

ApplicationContext自动到本地容器里找一个ApplicationEventMulticaster实现,如果没有自己new一个SimpleApplicationEventMulticaster。

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

大家可以看到如果给它一个executor(java.util.concurrent.Executor),它就可以异步支持发布事件了。

所以我们发送事件只需要通过ApplicationContext.publishEvent即可

监听器

具体代表者是:ApplicationListener

1、其继承自JDK的EventListener

package org.springframework.context;
 
import java.util.EventListener;
 
/**
 * Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * 

As of Spring 3.0, an ApplicationListener can generically declare the event type * that it is interested in. When registered with a Spring ApplicationContext, events * will be filtered accordingly, with the listener getting invoked for matching event * objects only. * * @author Rod Johnson * @author Juergen Hoeller * @param the specific ApplicationEvent subclass to listen to * @see org.springframework.context.event.ApplicationEventMulticaster */ public interface ApplicationListener extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }

我们在spring中自己实现的demo:

1.定义事件

import org.springframework.context.ApplicationEvent;

public class DemoEvent extends ApplicationEvent {
    private String channel;

    public DemoEvent(String channel) {
        super(channel);
        this.channel = channel;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }
}

2.实现目标(发布者)

@Service
@Lazy(false)
public class DemoPublisher implements ApplicationContextAware, DisposableBean {
    private ApplicationContext applicationContext;

    @Override
    public void destroy() throws Exception {
        applicationContext = null;
    }

    public void publish(DemoEvent demoEvent){
        applicationContext.publishEvent(demoEvent);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
3.实现监听器
方式一
@Service
public class DemoListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(DemoEvent demoEvent) {
        String channel = demoEvent.getChannel();
        System.out.println(channel);
    }
}
方式二
@Component
public class DemoAnnoListener {

    @Async
    @Order
    @EventListener(DemoEvent.class)
    public void listenWithAnnotation(DemoEvent event) {
        System.out.println(event.getChannel());
    }

}

好了,通过这3步,我们就可以在spring中愉快的实现事件-监听机制了。在我们需要发送事件时,只要调用 EventService的发送方法即可

demoPublisher.publish(new DemoEvent("weixin"));

使用@EventListener还支持SPEL表达式

@EventListener(condition = "#jkRecordConfirmEvent.jkRecordConfirmEventDTO.cwConfirmAndReturnFlag")
@Async("asyncListenerExecutor")

我们可以看到在监听器中,实现的方法使用了@Async注解。在spring3提供了@Aync注解来完成异步调用。我们可以使用这个新特性来完成异步调用。在实际项目中,我们一般也是把这两者结合来使用的,特别是监听事件是一件耗时过程时,这种方式降低了代码的耦合性,非常好用。