《spring设计思想》29-Event-事件发布与监听

java中自身设计的观察者模式涉及到的两个类

Observable;可以被观察的类

Observer:观察者接口

当我们想要使用观察者模式的时候,可以根据这两个类进行开发

public class ObservableDemo {
    public static void main(String[] args) {

        MyObservable observable = new MyObservable();
        observable.addObserver(new MyObserver());
        observable.setChanged();

    }


    static class MyObserver implements Observer{

        @Override
        public void update(Observable o, Object arg) {
            System.out.printf("监听到 对象:%s 发生了变化,传来消息 %s",o,arg);
        }
    }

    static class MyObservable extends Observable{

        @Override
        public synchronized void setChanged() {
            super.setChanged();
            super.notifyObservers("hello everybody");
            super.clearChanged();
        }
    }
}

观察者模式后来扩展为事件机制,不需要把观察者注册到被观察者这里,当被观察着发生变动,可以发布特定的事件,通知感兴趣的/订阅事件的类。

java的消息机制涉及到下面几个类:

EventObject:事件消息体

EventListener:事件监听接口

spring在设计消息事件机制的时候沿用了 java的设计体系:

Spring涉及到的几个比较重要的类

ApplicationEvent

ApplicationContextEvent

ApplicationListener

ApplicationEventMulticaster

简单的看下

ApplicationEvent extends EventObject 继承了java的事件机制

而ApplicationListener

继承了java的EventListener

而ApplicationEventPublisher 和ApplicationEventMutilcaster

主要指责就是发布事件,ApplicationEventMutilcaster的职责稍微多一点,就是监听器的维护

那么spring自带了几种类型的事件呢。

其实根据类的继承关系我们可以看到

ApplicationContextEvent 继承 ApplicationEvent

ApplicationContextEvent有四个子类

说明在上下文的声明周期中,会发出事件消息:

ContextRefreshEvent:上下文刷新事件

ContextStartEvent:上下文启动事件

ContextStopEvent:上下文停止事件

ContextCloseEvent:上下文关闭事件

我们可以简单的写个demo验证一下

public class SpringEventSequenceDemo implements ApplicationListener {
    public static void main(String[] args) {
        GenericApplicationContext applicationContext = new GenericApplicationContext();

        applicationContext.addApplicationListener(new SpringEventSequenceDemo());

        applicationContext.refresh();
        applicationContext.start();
        applicationContext.stop();
        applicationContext.close();

    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.printf("监听到事件:%s。\n",event);
    }
}

尤其注意先要refresh,才能start。这是ApplicationContext的状态决定的,而我们在开发当中,可能需要监听不同的事件做不同的事情,比如当上下文关闭的时候,需要我们关闭掉很多自定义的资源,释放空间。

我们自己实现的ApplicationListener的时候,还可以根据泛型参数实例,监听不同的事件,比如只关心ContextCloseEvent

class ContextCloaseEventListener implements ApplicationListener<ContextClosedEvent>{

        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            System.out.printf("监听到上线文关闭事件:%s。\n",event);
        }
    }

事件发布:

如果想要在开发过程中应用spring的事件机制发送事件,需要依靠ApplicationEventPublisher和ApplicationEventMulticaster.

所以我们我们在开发的时候可以借用spring的依赖注入注入ApplicationEventPublisher或者ApplicationEventMutilcater

或者是直接用上下文发布事件。

特别注意:spring的事件默认是单线程处理,要多线程处理需要以来@Async/@EnableAsync注解开始异步处理

特别特别注意:Spring的分层传播很有意思,在ParentContext发生的事件,在SubContext监听不到,在SubContext中发布的事件,在ParentContext中能监听到

public class SubContextEventDemo implements ApplicationListener<ContextClosedEvent> {
    public static void main(String[] args) {
        GenericApplicationContext currentContext = new GenericApplicationContext();
        currentContext.setId("currentContext");
        currentContext.addApplicationListener(new SubContextEventDemo());
        GenericApplicationContext parentContext = new GenericApplicationContext();
        parentContext.setId("parentContext");
        parentContext.addApplicationListener(new SubContextEventDemo());

        currentContext.setParent(parentContext);

        parentContext.refresh();

        currentContext.refresh();

        parentContext.close();

        currentContext.close();

    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.printf("监听到上下文关闭事件:%s。\n",event.getApplicationContext().getId());
    }
}

父子上下文各一个事件监听器,在关闭的时候

子上下文的关闭时间被父子上下文的监听器监听到:

所以要注意事件传播的范围。

另外还有一个注意的是,事件发布的节点。

比如,上下文注册监听器的事件是ApplicationContext.refresh()动作完成之前:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context. 初始化事件广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event. 发布ContextRefreshEvent
				finishRefresh();
			}
}

可以看到,spring自带的事件,最早的ContextRefreshEvent事件是在finishRefresh()里面触发的,而事件广播器是在initApplicationEventMutilcaster()初始化完成的,那事件监听器作为一个普通的bean,是在finishBeanFactoryInitialization(beanFactory)中实例化成bean的,所以正常的顺序是:

先初始化事件广播器 ApplicationEventMutilcaster

在实例化Bean 包括自定义事件监听器ApplicationListener

最后发布ContextRefreshEvent事件,这样看来是没有问题的,

但是在refresh()的前几步中:

处理了BeanFactoryPostProcessor 和 BeanPostProcessor的特殊类,如果自定义的bean即是BeanPostProcessor又是ApplicationEventPublisher,这个时候就要注意:

如果在BeanPostProcessor或者在BeanFactoryPostProcessor中发布事件,这些事件会先存储在Application中,等到finishRefresh的时候,先发布早起的事件,再发布正常的上下文事件。

public class EarlyEventPublisherDemo implements ApplicationListener, BeanPostProcessor {

    @Autowired
    ApplicationContext applicationContext;

    @PostConstruct
    public void publisherEvent(){
        applicationContext.publishEvent("Early Event published");
    }
    public static void main(String[] args) {
        GenericApplicationContext genericApplicationContext = new AnnotationConfigApplicationContext(EarlyEventPublisherDemo.class);

        genericApplicationContext.close();
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.printf("监听到事件:%s。\n",event);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值