先了解观察者模式
在讲解spring监听器之前,我们先了解一下观察者模式,spring的监听模式是基于这个模式开发的。观察者模式定义一系列对象之间的一对多关系,当一个对象改变、更新状态时,依赖它的都会收到通知改变或者更新。
角色:
1、抽象主题(Subject):
它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
2、具体主题(Concrete Subject):
将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
3、抽象观察者(Observer):
为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
4、具体观察者(Concrete Observer):
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
示例代码:
主题(Subject)
/**
* 主题
*/
public interface Subject {
void multicastEvent(Event event);//发布事件
void addObserver(Observer observer);//添加观察者
}
具体主题(Concrete Subject)
/**
* 具体主题
*/
public class ConcreteSubject implements Subject {
private List<Observer> observers;
public ConcreteSubject(){
observers = new CopyOnWriteArrayList<Observer>();
}
@Override
public void multicastEvent(Event event) {
for(Observer observer : observers){
invokeObserver(observer,event);
}
}
private void invokeObserver(Observer observer,Event event){
observer.update(event);
}
public void addObserver(Observer observer){
observers.add(observer);
}
}
观察者(Observer)
/**
* 观察者
*/
public interface Observer {
void update(Event event);
}
具体观察者(Concrete Observer)
/**
* 具体观察者
*/
public class ConcreteObserver implements Observer {
@Override
public void update(Event event) {
System.out.println("now,the state is "+event.getState());
}
}
观察的事件
/**
* 被观察的事件
*/
public interface Event {
int getState();
}
public class AbstractEvent implements Event {
int state;
@Override
public int getState() {
return state;
}
public void setState(int state){
this.state=state;
}
}
测试代码:
public class ObserverTest {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.addObserver(observer);
AbstractEvent event = new AbstractEvent();
System.out.println("event state is "+event.getState());
event.setState(10);
subject.multicastEvent(event);
}
}
结果是:
event state is 0
now,the state is 10
spring的监听模式
spring的监听逻辑是把我们创建的监听器(实现了ApplicationListener 接口的)添加到组播器中,然后在我们不同的时期发布事件就实际调用的是组播器的发布事件,通过注册在组播器中的监听和传入的事件进行匹配,调用满足条件的监听器(监听改事件的监听器),触发监听器的方法。
我们先看入口
发布事件:
//在AbstractApplicationContext类中,不同的生命周期会有不同的事件发布
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
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<Object>(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);
}
}
}
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);这一段代码是调用组播器的发布事件。
下面我们看下组播器是如何发布事件的,代码在类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);
}
}
}
实际调用监听器的方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
自己实现一个监听
1.因为spring的监听是针对事件来监听的,首先实现一个符合自己要求的事件
public class LzlEvent extends ApplicationContextEvent {
private Object object;
/**
* Create a new ContextStartedEvent.
*
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public LzlEvent(ApplicationContext source,Object o) {
this(source);
object = o;
}
public LzlEvent(ApplicationContext source) {
super(source);
}
public Object getObject(){
return object;
}
}
2.实现自己的监听器
public class LzlListener implements ApplicationListener<LzlEvent> {
@Override
public void onApplicationEvent(LzlEvent event) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Person person = (Person) event.getObject();
System.out.println("listener has received a object:"+person);
}
}
3.比较关键的一步,我们需要有一个触发我们自定义事件的方法,利用上下文容器提供的发布事件的能力(所以我们要获取到上下文,实现ApplicationContextAware接口)
public class LzlContext implements ApplicationContextAware {
static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public ApplicationContext getContext(){
return context;
}
public static void publishEvent(Object o){
context.publishEvent(new LzlEvent(context,o));
}
}
现在我们就可以测试我们的监听器是否可行了
public class TestListener {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
Person person = new Person();
person.setAge(18);
person.setName("lzl");
person.setSex("man");
LzlContext.publishEvent(person);
System.out.println("the main has finished!");
}
}
写到这里其实正常功能已经实现了,但是我发现一个问题,就是我当前实现的这个监听器在我们发布事件后只能同步执行,也就是说我们必须等待监听器执行完成之后主流程才能继续,感觉这并不是我想要的监听器。然后发现问题就出在组播器发布事件的方法上即multicastEvent()方法,这个方法判断当前组播器有没有线程池,有就放入线程池异步执行,没有就同步,它也提供了一个setTaskExecutor方法,但是实际上我们至始至终都没有传线程池给组播器,所以就一直走的是同步执行监听方法,而上下文容器(AbstractApplicationContext)并没有提供获取组播器的方法。找了好久终于看到上下文提供我们可以自定义组播器的方法。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
虽然自己实现一个组播器不太容易,但是我们可以使用继承大法,然后添加自己需要的线程池就行了
//我们可以在创建这个组播器的bean时就传入一个线程池
public class LzlApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
public LzlApplicationEventMulticaster() {
Executor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors(),0, TimeUnit.SECONDS,new ArrayBlockingQueue<>(64));
super.setTaskExecutor(executor);
}
}
这样我们就可以实现异步执行监听。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--<bean class="com.lzl.springscope.LzlBeanFactoryPostProcessor"/>-->
<bean class="com.lzl.springscope.LzlContext"/>
<bean class="com.lzl.springscope.LzlListener"/>
<bean name="applicationEventMulticaster" class="com.lzl.springscope.LzlApplicationEventMulticaster"/>
<!--<bean name="lzlBean" class="com.lzl.springscope.LzlBean" scope="lzlScope"></bean>-->
</beans>
最后附上测试结果
the main has finished!
listener has received a object:Person{name='lzl', sex='man', age=18}