
前言
在看 Spring 源码时一定能看到这么几个类:ApplicationEvent、ApplicationListener、ApplicationEventPublisher、ApplicationEventPublisherAware,这几个类就是 Spring 事件机制的基础。其实 Spring 的事件机制是增强于 JDK 的 Event,从类结构上也能看出:
在讲 Spring 的事件机制之前,我们需要搞清楚什么是事件机制?为什么要有事件机制?
举个例子:
系统用户注册后需要给用户发送邮件和手机短信,常规操作可能是在用户注册方法最后增加 sendMail 和 sendMsg 方法调用。但如果某一天又增加了发送微信消息,那就又要改原来用户注册方法的逻辑,这是不合理的,因为发送邮件、短信等不是用户注册的核心流程。违背了高内聚低耦合原则。所以这就出现了事件机制,用户注册后发布一个用户注册成功的事件,系统维护多个该事件的监听者,监听到这个事件后做自己的逻辑处理,用户注册逻辑只关注用户注册的事情,不用管其它监听者的逻辑。不知道这样说明白了没有!!!
JDK 事件机制
结合上述例子,我们看下在原生 Java 中怎么实现事件逻辑:
1 | // --------------- 用户注册事件 --------------- |
整合 Spring 事件机制
同样的上述问题,整合 Spring 要怎么使用呢?
RegisterEvent.java(改成继承 ApplicationEvent)
1 | import org.springframework.context.ApplicationEvent; |
MailListener.java 和 MsgListener.java(改成继承 ApplicationListener)
1 | import org.springframework.context.ApplicationListener; |
RegisterListenerPublisher.java(改成实现 ApplicationEventPublisherAware)
1 | import org.springframework.context.ApplicationEventPublisher; |
单元测试:
1 |
|
整合前后区别
不知读者能否看出上述 JDK 和 Spring 处理的区别?
其实最大的区别就是 Spring 根据 @Compent 注解扫描监听器,并为我们管理了这些监听器,在发布事件时,又通过泛型找到需要符合条件的监听器。这也就是 SpringIOC 带给我们最大的方便,不用自己管理 Bean。
源码剖析
监听器的扫描
监听器是一个实现了 ApplicationListener 的类,所以这个类被 Spring 扫描到后也会跟 IOC 管理的普通类一样转化成 BeanDefinition,然后因为默认是单例的,所以又会执行实例化相关操作。下面一步一步看 IOC 容器是怎么扫描监听器的。
1,IOC 容器启动会执行 refresh 方法(这里我把源码中的注释都去掉了),在 refresh 方法中会调用 registerListeners()方法:
1 |
|
2,在 registerListeners()方法中,会根据 ApplicationListener.class 类型去 BeanFactory 中找到所有实现了 ApplicationListener 的 beanName。
1 | protected void registerListeners() { |
3,然后调用 getApplicationEventMulticaster().addApplicationListenerBean()方法将 beanName 存到 ListenerRetriever 里的 applicationListenerBeans 集合中。
1 |
|
1 | private class ListenerRetriever { |
到这步 BeanFactory 已经保存了所有的监听器。但还只是保存了监听器的名称,还没有保存 Bean 对象。
4,像普通的单例 Bean 在容器初始化时都会实例化单例对象,实例化对象会调用到 AbstractAutowireCapableBeanFactory#createBean()方法,然后又会调用到 AbstractAutowireCapableBeanFactory#doCreateBean()方法。在 doCreateBean()方法中创建对象,并执行属性注入等方法,其中就包括 initializeBean 方法:
1 | try { |
在 initializeBean 方法中,会调用到 aware 注入,执行 initMethod 方法等操作,最后会执行 applyBeanPostProcessorsAfterInitialization()方法:
1 | protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { |
applyBeanPostProcessorsAfterInitialization()方法里循环所有的 BeanPostProcessor 执行 postProcessAfterInitialization 方法,BeanPostProcessor 在 SpringBean 生命周期中是非常重要的一个接口,AOP 就是基于 BeanPostProcessor 的 postProcessAfterInitialization 方法将实例化出的 Bean 替换成代理对象。
5,在众多的 BeanPostProcessor 类中,有个 ApplicationListenerDetector 类,就是专门处理监听器的。上面的循环自然也会执行到 ApplicationListenerDetector#postProcessAfterInitialization()方法。在 postProcessAfterInitialization 方法中判断 Bean 是否是 ApplicationListener 类型,如果是的话就会将其添加到上面讲的 ListenerRetriever 类里的 applicationListeners 集合中。
1 |
|
至此,SpringIOC 容器就算是把监听器扫描完成了。
ApplicationEventPublisherAware 的注入
ApplicationEventPublisherAware 类看名字就知道它是实现与 Aware 接口的,同样这个类的注入方法 setApplicationEventPublisher() 也是通过 BeanPostProcessor 调用的,在 IOC 容器中有个专门处理 Aware 类的 BeanPostProcessor,它是 ApplicationContextAwareProcessor,会处理多种的 Aware。
1 | private void invokeAwareInterfaces(Object bean) { |
事件发布
通过上面的代码可以看出,setApplicationEventPublisher 方法传入的是 applicationContext,所以调用 ApplicationEventPublisher 的 publishEvent 方法调用到的是 AbstractApplicationContext#publishEvent():
1 | protected void publishEvent(Object event, @Nullable ResolvableType eventType) { |
内部会调用 getApplicationEventMulticaster().multicastEvent(),在 multicastEvent 方法中找到符合条件的所有监听器,并循环执行 onApplicationEvent()方法,最后就是具体的业务逻辑了。
1 |
|
另外,在找符合对应事件类型的监听器方法时,Spring 还用到了缓存:
1 | protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) { |
总结
看完上面的源码不知道读者有什么感想。
我感觉每次研究 Spring 源码每次都有新的收获,一是 Spring 源码确实非常庞大,二是感叹 Spring 源码确实扩展性很强。如果说 MyBatis 是操作数据库的,Struts 是处理 Web 请求的,那 Spring 就是平台,IOC 不做任何事,只管理 Bean,但开发者又能通过 Spring 提供的扩展做任何事。