先了解观察者模式

在讲解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}