能解耦,是多么重要的一件事情!
摔杯为号、看我眼色行事、见南面火起,这是在嘎哈么?这其实是在通过事物传播进行解耦引线和炸弹,仅仅是这样的一个解耦,它放到了多少村夫莽汉,劫了法场,篡了兵权!
这样的解耦场景在互联网开发的设计中使用的也是非常频繁,如:这里需要一个注册完成事件推送消息、用户下单我会发送一个MQ、收到我的支付消息就可以发货了等等,都是依靠事件订阅和发布以及MQ消息这样的组件,来处理系统之间的调用解耦,最终通过解耦的方式来提升整体系统架构的负载能力。
其实解耦思路可以理解为设计模式中观察者模式的具体使用效果,在观察者模式中当对象间存在一对多关系时,则使用观察者模式,它是一种定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这让我想起了我每个月的车牌摇号,都会推送给我一条本月没中签的消息!!!
在 Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及监听事件来完成一些自定义的动作。比如你可以定义一个新用户注册的事件,当有用户执行注册完成后,在事件监听中给用户发送一些优惠券和短信提醒,这样的操作就可以把属于基本功能的注册和对应的策略服务分开,降低系统的耦合。以后在扩展注册服务,比如需要添加风控策略、添加实名认证、判断用户属性等都不会影响到依赖注册成功后执行的动作。
那么在本章节我们需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能,最终可以让我们在现有实现的 Spring 框架中可以定义、监听和发布自己的事件信息。
其实事件的设计本身就是一种观察者模式的实现,它所要解决的就是一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
在功能实现上我们需要定义出事件类、事件监听、事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。整体设计结构如下图:
- small-spring-step-10
- └── src
- ├── main
- │ └── java
- │ └── cn.bugstack.springframework
- │ ├── beans
- │ │ ├── factory
- │ │ │ ├── config
- │ │ │ │ ├── AutowireCapableBeanFactory.java
- │ │ │ │ ├── BeanDefinition.java
- │ │ │ │ ├── BeanFactoryPostProcessor.java
- │ │ │ │ ├── BeanPostProcessor.java
- │ │ │ │ ├── BeanReference.java
- │ │ │ │ ├── ConfigurableBeanFactory.java
- │ │ │ │ └── SingletonBeanRegistry.java
- │ │ │ ├── support
- │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
- │ │ │ │ ├── AbstractBeanDefinitionReader.java
- │ │ │ │ ├── AbstractBeanFactory.java
- │ │ │ │ ├── BeanDefinitionReader.java
- │ │ │ │ ├── BeanDefinitionRegistry.java
- │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
- │ │ │ │ ├── DefaultListableBeanFactory.java
- │ │ │ │ ├── DefaultSingletonBeanRegistry.java
- │ │ │ │ ├── DisposableBeanAdapter.java
- │ │ │ │ ├── FactoryBeanRegistrySupport.java
- │ │ │ │ ├── InstantiationStrategy.java
- │ │ │ │ └── SimpleInstantiationStrategy.java
- │ │ │ ├── support
- │ │ │ │ └── XmlBeanDefinitionReader.java
- │ │ │ ├── Aware.java
- │ │ │ ├── BeanClassLoaderAware.java
- │ │ │ ├── BeanFactory.java
- │ │ │ ├── BeanFactoryAware.java
- │ │ │ ├── BeanNameAware.java
- │ │ │ ├── ConfigurableListableBeanFactory.java
- │ │ │ ├── DisposableBean.java
- │ │ │ ├── FactoryBean.java
- │ │ │ ├── HierarchicalBeanFactory.java
- │ │ │ ├── InitializingBean.java
- │ │ │ └── ListableBeanFactory.java
- │ │ ├── BeansException.java
- │ │ ├── PropertyValue.java
- │ │ └── PropertyValues.java
- │ ├── context
- │ │ ├── event
- │ │ │ ├── AbstractApplicationEventMulticaster.java
- │ │ │ ├── ApplicationContextEvent.java
- │ │ │ ├── ApplicationEventMulticaster.java
- │ │ │ ├── ContextClosedEvent.java
- │ │ │ ├── ContextRefreshedEvent.java
- │ │ │ └── SimpleApplicationEventMulticaster.java
- │ │ ├── support
- │ │ │ ├── AbstractApplicationContext.java
- │ │ │ ├── AbstractRefreshableApplicationContext.java
- │ │ │ ├── AbstractXmlApplicationContext.java
- │ │ │ ├── ApplicationContextAwareProcessor.java
- │ │ │ └── ClassPathXmlApplicationContext.java
- │ │ ├── ApplicationContext.java
- │ │ ├── ApplicationContextAware.java
- │ │ ├── ApplicationEvent.java
- │ │ ├── ApplicationEventPublisher.java
- │ │ ├── ApplicationListener.java
- │ │ └── ConfigurableApplicationContext.java
- │ ├── core.io
- │ │ ├── ClassPathResource.java
- │ │ ├── DefaultResourceLoader.java
- │ │ ├── FileSystemResource.java
- │ │ ├── Resource.java
- │ │ ├── ResourceLoader.java
- │ │ └── UrlResource.java
- │ └── utils
- │ └── ClassUtils.java
- └── test
- └── java
- └── cn.bugstack.springframework.test
- ├── event
- │ ├── ContextClosedEventListener.java
- │ ├── ContextRefreshedEventListener.java
- │ ├── CustomEvent.java
- │ └── CustomEventListener.java
- └── ApiTest.java
工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码
容器事件和事件监听器实现类关系,如图 11-2
图 10-2
cn.bugstack.springframework.context.ApplicationEvent
- public abstract class ApplicationEvent extends EventObject {
- /**
- * Constructs a prototypical Event.
- *
- * @param source The object on which the Event initially occurred.
- * @throws IllegalArgumentException if source is null.
- */
- public ApplicationEvent(Object source) {
- super(source);
- }
- }
cn.bugstack.springframework.context.event.ApplicationContextEvent
- public class ApplicationContextEvent extends ApplicationEvent {
- /**
- * Constructs a prototypical Event.
- *
- * @param source The object on which the Event initially occurred.
- * @throws IllegalArgumentException if source is null.
- */
- public ApplicationContextEvent(Object source) {
- super(source);
- }
- /**
- * Get the <code>ApplicationContext</code> that the event was raised for.
- */
- public final ApplicationContext getApplicationContext() {
- return (ApplicationContext) getSource();
- }
- }
cn.bugstack.springframework.context.event.ContextClosedEvent
- public class ContextClosedEvent extends ApplicationContextEvent{
- /**
- * Constructs a prototypical Event.
- *
- * @param source The object on which the Event initially occurred.
- * @throws IllegalArgumentException if source is null.
- */
- public ContextClosedEvent(Object source) {
- super(source);
- }
- }
cn.bugstack.springframework.context.event.ContextRefreshedEvent
- public class ContextRefreshedEvent extends ApplicationContextEvent{
- /**
- * Constructs a prototypical Event.
- *
- * @param source The object on which the Event initially occurred.
- * @throws IllegalArgumentException if source is null.
- */
- public ContextRefreshedEvent(Object source) {
- super(source);
- }
- }
cn.bugstack.springframework.context.event.ApplicationEventMulticaster
- public interface ApplicationEventMulticaster {
- /**
- * Add a listener to be notified of all events.
- * @param listener the listener to add
- */
- void addApplicationListener(ApplicationListener<?> listener);
- /**
- * Remove a listener from the notification list.
- * @param listener the listener to remove
- */
- void removeApplicationListener(ApplicationListener<?> listener);
- /**
- * Multicast the given application event to appropriate listeners.
- * @param event the event to multicast
- */
- void multicastEvent(ApplicationEvent event);
- }
cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster
- public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
- public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
- private BeanFactory beanFactory;
- @Override
- public void addApplicationListener(ApplicationListener<?> listener) {
- applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
- }
- @Override
- public void removeApplicationListener(ApplicationListener<?> listener) {
- applicationListeners.remove(listener);
- }
- @Override
- public final void setBeanFactory(BeanFactory beanFactory) {
- this.beanFactory = beanFactory;
- }
- protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
- LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
- for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
- if (supportsEvent(listener, event)) allListeners.add(listener);
- }
- return allListeners;
- }
- /**
- * 监听器是否对该事件感兴趣
- */
- protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
- Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
- // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
- Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
- Type genericInterface = targetClass.getGenericInterfaces()[0];
- Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
- String className = actualTypeArgument.getTypeName();
- Class<?> eventClassName;
- try {
- eventClassName = Class.forName(className);
- } catch (ClassNotFoundException e) {
- throw new BeansException("wrong event class name: " + className);
- }
- // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
- // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
- return eventClassName.isAssignableFrom(event.getClass());
- }
- }
supportsEvent 方法运行截图
cn.bugstack.springframework.context.ApplicationEventPublisher
- public interface ApplicationEventPublisher {
- /**
- * Notify all 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
- */
- void publishEvent(ApplicationEvent event);
- }
- public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
- public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
- private ApplicationEventMulticaster applicationEventMulticaster;
- @Override
- public void refresh() throws BeansException {
- // 6. 初始化事件发布者
- initApplicationEventMulticaster();
- // 7. 注册事件监听器
- registerListeners();
- // 9. 发布容器刷新完成事件
- finishRefresh();
- }
- private void initApplicationEventMulticaster() {
- ConfigurableListableBeanFactory beanFactory = getBeanFactory();
- applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
- beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
- }
- private void registerListeners() {
- Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
- for (ApplicationListener listener : applicationListeners) {
- applicationEventMulticaster.addApplicationListener(listener);
- }
- }
- private void finishRefresh() {
- publishEvent(new ContextRefreshedEvent(this));
- }
- @Override
- public void publishEvent(ApplicationEvent event) {
- applicationEventMulticaster.multicastEvent(event);
- }
- @Override
- public void close() {
- // 发布容器关闭事件
- publishEvent(new ContextClosedEvent(this));
- // 执行销毁单例bean的销毁方法
- getBeanFactory().destroySingletons();
- }
- }
cn.bugstack.springframework.test.event.CustomEvent
- public class CustomEvent extends ApplicationContextEvent {
- private Long id;
- private String message;
- /**
- * Constructs a prototypical Event.
- *
- * @param source The object on which the Event initially occurred.
- * @throws IllegalArgumentException if source is null.
- */
- public CustomEvent(Object source, Long id, String message) {
- super(source);
- this.id = id;
- this.message = message;
- }
- // ...get/set
- }
cn.bugstack.springframework.test.event.CustomEventListener
- public class CustomEventListener implements ApplicationListener<CustomEvent> {
- @Override
- public void onApplicationEvent(CustomEvent event) {
- System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());
- System.out.println("消息:" + event.getId() + ":" + event.getMessage());
- }
- }
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <bean class="cn.bugstack.springframework.test.event.ContextRefreshedEventListener"/>
- <bean class="cn.bugstack.springframework.test.event.CustomEventListener"/>
- <bean class="cn.bugstack.springframework.test.event.ContextClosedEventListener"/>
- </beans>
- public class ApiTest {
- @Test
- public void test_event() {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
- applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));
- applicationContext.registerShutdownHook();
- }
- }
测试结果
- 刷新事件:cn.bugstack.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$440a36f5
- 收到:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@71c7db30消息;时间:22:32:50
- 消息:1019129009086763:成功了!
- 关闭事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d
- Process finished with exit code 0
六、总结
【编辑推荐】
免责声明:本站发布的内容(图片、视频和文字)以原创、来自互联网转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系QQ:712375056 进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
Copyright © 2009-2021 56dr.com. All Rights Reserved. 特网科技 特网云 版权所有 珠海市特网科技有限公司 粤ICP备16109289号
域名注册服务机构:阿里云计算有限公司(万网) 域名服务机构:烟台帝思普网络科技有限公司(DNSPod) CDN服务:阿里云计算有限公司 中国互联网举报中心 增值电信业务经营许可证B2
建议您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流浏览器浏览本网站