手写Spring:第11章-容器事件和事件监听器

文章目录

  • 一、目标:容器事件和事件监听器
  • 二、设计:容器事件和事件监听器
  • 三、实现:容器事件和事件监听器
    • 3.1 工程结构
    • 3.2 容器事件和事件监听器类图
    • 3.3 定义和实现事件
      • 3.3.1 定义事件抽象类
      • 3.3.2 定义应用上下文事件实现类
      • 3.3.3 上下文刷新事件类
      • 3.3.4 上下文关闭事件类
    • 3.4 事件监听器
      • 3.4.1 类工具类添加判断代理对象
      • 3.4.2 事件监听器
    • 3.5 事件广播器
      • 3.5.1 事件广播器接口
      • 3.5.2 事件广播器抽象类
      • 3.5.3 简单事件广播器
    • 3.6 事件发布者的定义和实现
      • 3.6.1 定义事件发布者
      • 3.6.2 应用上下文接口
      • 3.6.3 应用上下文抽象类
  • 四、测试:容器事件和事件监听器
    • 4.1 添加测试配置
      • 4.1.1 创建自定义事件
      • 4.1.2 自定义事件监听器
      • 4.1.3 自定义事件刷新监听器
      • 4.1.4 自定义事件关闭监听器
      • 4.1.5 配置文件
    • 4.2 单元测试
  • 五、总结:容器事件和事件监听器

一、目标:容器事件和事件监听器

💡 如何处理解耦?什么是事件?如何使用事件功能?

  • Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及监听事件来完成一些自定义的动作。
    • 比如:你可以定义一个新用户注册的事件,当有用户执行注册完成后,在事件监听中给用户发送一些优惠券和短信提醒。
    • 这样的操作就可以把属于基本功能的注册和对应的策略服务分开,降低系统的耦合。
    • 以后再扩展注册服务,比如需要添加风控策略、添加实名认证、判断用户属性等都不会影响到依赖注册成功后执行的动作。
  • 本节需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能。
    • 最终可以让 Spring 框架可以定义、监听和发布自己的事件信息。

二、设计:容器事件和事件监听器

💡 设计:观察者模式,实现 Spring Event 容器时间和事件监听器。

  • 事件 的设计本身就是一种观察者模式的实现,它所要解决的就是一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
  • 再功能上我们需要定义:事件类、事件监听、事件发布。
    • 这些类的功能需要结合到 SpringAbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。

在这里插入图片描述

  • 在整个功能实现过程中,需要在面向用户的应用上下文 AbstractApplicationContext 中添加相关事件内容。
    • 包括:初始化事件发布者、注册事件监听器、发布容器刷新完成事件。
  • 使用观察者模式定义事件类、监听类、发布类,同时还需要完成一个广播器的功能。
    • 接收到事件推送时进行分析处理符合监听事件接收者感兴趣的事件,也就是使用 isAssignableFrom 进行判断。
  • isAssignableFrominstanceof 相似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系。
    • 默认所有的类的终极父类都是 Object
    • 如果 A.isAssignableFrom(B) 结果是 true,证明 B 可以转换成 A,也就是 A 可以由 B 转换而来。

三、实现:容器事件和事件监听器

3.1 工程结构

spring-step-10
|-src|-main|	|-java|		|-com.lino.springframework|			|-beans|			|	|-factory|			|	|	|-config|			|	|	|	|-AutowireCapableBeanFactory.java|			|	|	|	|-BeanDefinition.java|			|	|	|	|-BeanFactoryPostProcessor.java|			|	|	|	|-BeanPostProcessor.java|			|	|	|	|-BeanReference.java|			|	|	|	|-ConfigurableBeanFactory.java|			|	|	|	|-SingletonBeanRegistry.java|			|	|	|-support|			|	|	|	|-AbstractAutowireCapableBeanFactory.java|			|	|	|	|-AbstractBeabDefinitionReader.java|			|	|	|	|-AbstractBeabFactory.java|			|	|	|	|-BeabDefinitionReader.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|			|	|	|-HierarcgicalBeanFactory.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|			|-util|			|	|-ClassUtils.java|-test|-java|-com.lino.springframework.test|-event|	|-CustomClosedEventListener.java|	|-CustomEvent.java|	|-CustomEventListener.java|	|-CustomRefreshedEventListener.java|-ApiTest.java|-resources|-spring.xml

3.2 容器事件和事件监听器类图

在这里插入图片描述

  • 整个类图以围绕实现 event 事件定义、发布、监听功能实现和把事件相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。
  • 在实现的过程中主要以扩展 spring context 包为主,事件的实现也是在这个包下进行扩展的,当然也可以看出来目前所有的实现内容,仍然是以 IOC 为主。
  • ApplicationContext 容器继承事件发布功能接口 ApplicationEventPublisher,并在实现类中提供事件监听功能。
  • ApplicationEventMulticaster 接口是注册监听器和发布事件的广播器,提供添加、移除和发布事件方法。
  • 最后是发布容器关闭事件,这个仍然需要扩展到 AbstractApplicationContext#close 方法中,由注册到虚拟机的钩子实现。

3.3 定义和实现事件

3.3.1 定义事件抽象类

ApplicationEvent.java

package com.lino.springframework.context;import java.util.EventObject;/*** @description: 定义事件抽象类*/
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);}
}
  • 以继承 java.util.EventObject 定义出具备事件功能的抽象类 ApplicationEvent,后续所有事件的类都需要继承这个类。

3.3.2 定义应用上下文事件实现类

ApplicationContextEvent.java

package com.lino.springframework.context.event;import com.lino.springframework.context.ApplicationContext;
import com.lino.springframework.context.ApplicationEvent;/*** @description: 定义应用上下文事件实现类*/
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);}/*** 获取应用上下文** @return 应用上下文*/public final ApplicationContext getApplicationContext() {return (ApplicationContext) getSource();}
}

3.3.3 上下文刷新事件类

ContextRefreshedEvent.java

package com.lino.springframework.context.event;/*** @description: 上下文刷新事件类*/
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);}
}

3.3.4 上下文关闭事件类

ContextClosedEvent.java

package com.lino.springframework.context.event;/*** @description: 上下文关闭事件类*/
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);}
}
  • ApplicationContextEvent 是定义事件的类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。
  • ContextRefreshedEventContextClosedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。

3.4 事件监听器

3.4.1 类工具类添加判断代理对象

ClassUtils.java

package com.lino.springframework.util;/*** @description: 类工具类*/
public class ClassUtils {/*** 获取默认类加载器** @return 类加载器*/public static ClassLoader getDefaultClassLoader() {ClassLoader cl = null;try {cl = Thread.currentThread().getContextClassLoader();} catch (Throwable ex) {// Cannot access thread context ClassLoader - falling back to system class loader...}if (cl == null) {// No thread context class loader -> use class loader of this class.cl = ClassUtils.class.getClassLoader();}return cl;}/*** 判断类是否是cglib代理对象** @param clazz 类* @return 是否是cglib代理对象*/public static boolean isCglibProxyClass(Class<?> clazz) {return (clazz != null && isCglibProxyClassName(clazz.getName()));}/*** 判断类名是否符合cglib代理类命名** @param className 类名* @return 是否符合cglib代理类命名*/public static boolean isCglibProxyClassName(String className) {return (className != null && className.contains("$$"));}
}
  • 添加判断是否是 cglib 代理对象和是否符合 cglib 代理名称的方法。

3.4.2 事件监听器

ApplicationListener.java

package com.lino.springframework.context;import java.util.EventListener;/*** @description: 事件监听器* @author: lingjian* @createDate: 2022/12/1 15:01*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {/*** 处理事件** @param event 事件*/void onApplicationEvent(E event);
}
  • 添加监听器开放接口,后续开放给外部实现调用。

3.5 事件广播器

3.5.1 事件广播器接口

ApplicationEventMulticaster.java

package com.lino.springframework.context.event;import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;/*** @description: 事件广播器*/
public interface ApplicationEventMulticaster {/*** 添加监听器** @param listener 监听器*/void addApplicationListener(ApplicationListener<?> listener);/*** 删除监听器** @param listener 监听器*/void removeApplicationListener(ApplicationListener<?> listener);/*** 广播事件** @param event 事件*/void multicastEvent(ApplicationEvent event);
}
  • 在事件广播器中定义了添加监听和删除监听的方法以及一个广播事件的方法 multicastEvent
  • 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。

3.5.2 事件广播器抽象类

AbstractApplicationEventMulticaster.java

package com.lino.springframework.context.event;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.util.ClassUtils;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;/*** @description: 事件广播器抽象类*/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();private BeanFactory beanFactory;@Overridepublic void addApplicationListener(ApplicationListener<?> listener) {applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);}@Overridepublic void removeApplicationListener(ApplicationListener<?> listener) {applicationListeners.remove(listener);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}/*** 获取事件监听器** @param event 事件* @return 监听器列表*/protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {LinkedList<ApplicationListener> allListeners = new LinkedList<>();for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {if (supportsEvent(listener, event)) {allListeners.add(listener);}}return allListeners;}/*** 监听器是否该事件感兴趣** @param applicationListener 监听器* @param event               事件* @return 是否感兴趣*/protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();// 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 classClass<?> 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());}
}
  • AbstractApplicationEventMulticaster 是对事件广播器的公用方法提取,在这个类中可以实现一些基本功能,避免所有直接实现接口还需要处理细节。
  • 除了像 addApplicationListenerremoveApplicationListener 这样的通用方法。这里主要是对 getApplicationListenerssupportsEvent 的处理。
    • getApplicationListeners:主要是摘取符合广播事件中的监听处理器,具体过滤动作在 supportsEvent 方法中。
    • supportsEvent
      • 主要包括对 Cglib、Simple 不同实例化需要获取父类的 ClassCglib 代理类需要获取父类的 Class。普通实例化则不需要。
      • 接下来就是通过提取接口和对应的 ParameterizedTypeeventClassName,方便最后确认是否为子类和父类的关系,以此证明此事件归这个符合的类处理。

在这里插入图片描述

  • 从结果看出,最终 eventClassNameevent.getClass()isAssignableFrom 判断下为 true
  • 关于 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 可以尝试在 AbstractApplicationContext 类中更换验证。

3.5.3 简单事件广播器

SimpleApplicationEventMulticaster.java

package com.lino.springframework.context.event;import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;/*** @description: 简单事件广播器*/
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {setBeanFactory(beanFactory);}@SuppressWarnings("unchecked")@Overridepublic void multicastEvent(final ApplicationEvent event) {for (final ApplicationListener listener : getApplicationListeners(event)) {listener.onApplicationEvent(event);}}
}

3.6 事件发布者的定义和实现

3.6.1 定义事件发布者

ApplicationEventPublisher.java

package com.lino.springframework.context;/*** @description: 事件发布者接口*/
public interface ApplicationEventPublisher {/*** 发布事件** @param event 事件*/void publishEvent(ApplicationEvent event);
}
  • ApplicationEventPublisher 是一个事件的发布接口,所有的事件都需要从这个接口发布出去。

3.6.2 应用上下文接口

ApplicationContext.java

package com.lino.springframework.context;import com.lino.springframework.beans.factory.HierarchicalBeanFactory;
import com.lino.springframework.beans.factory.ListableBeanFactory;
import com.lino.springframework.core.io.ResourceLoader;/*** @description: 应用上下文接口*/
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader, ApplicationEventPublisher {
}

3.6.3 应用上下文抽象类

AbstractApplicationContext.java

package com.lino.springframework.context.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.context.ApplicationEvent;
import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.context.ConfigurableApplicationContext;
import com.lino.springframework.context.event.ApplicationEventMulticaster;
import com.lino.springframework.context.event.ContextClosedEvent;
import com.lino.springframework.context.event.ContextRefreshedEvent;
import com.lino.springframework.context.event.SimpleApplicationEventMulticaster;
import com.lino.springframework.core.io.DefaultResourceLoader;
import java.util.Collection;
import java.util.Map;/*** @description: 抽象应用上下文*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";private ApplicationEventMulticaster applicationEventMulticaster;@Overridepublic void refresh() throws BeansException {// 1.创建 BeanFactory,并加载 BeanDefinitionrefreshBeanFactory();// 2.获取 BeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();// 3.添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContextbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 4.在 Bean 实例化之前,执行 BeanFactoryPostProcessinvokeBeanFactoryPostProcessor(beanFactory);// 5.BeanPostProcessor 需要提前与其他 Bean 对象实例化之前执行注册操作registerBeanPostProcessor(beanFactory);// 6.初始化事件发布者initApplicationEventMulticaster();// 7.注册事件监听器registerListeners();// 8.提前实例化单例 Bean 对象beanFactory.preInstantiateSingletons();// 9.发布容器刷新完成事件finishRefresh();}...private void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingletonBean(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));}@Overridepublic void publishEvent(ApplicationEvent event) {applicationEventMulticaster.multicastEvent(event);}...@Overridepublic void close() {// 发布容器关闭事件publishEvent(new ContextClosedEvent(this));// 执行销毁单例bean的销毁方法getBeanFactory().destroySingletons();}}
  • 在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件发布者注册事件监听器发布容器刷新完成事件。三个方法用于处理事件操作。
    • 初始化事件发布者(initApplicationEventMulticaster):主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。
    • 注册事件监听器(registerListeners):通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
    • 发布容器刷新完成事件(finishRefresh):发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event) 方法。
    • 关闭方法(close):新增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this))

四、测试:容器事件和事件监听器

4.1 添加测试配置

4.1.1 创建自定义事件

CustomEvent.java

package com.lino.springframework.test.event;import com.lino.springframework.context.event.ApplicationContextEvent;/*** @description: 自定义事件*/
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;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}
  • 创建一个自定义事件,在事件类的构造函数中可以添加自己想要的入参信息。这个事件类最终会被完成的监听类监听到,所以你添加的属性也会被获得到。

4.1.2 自定义事件监听器

CustomEventListener.java

package com.lino.springframework.test.event;import com.lino.springframework.context.ApplicationListener;
import java.util.Date;/*** @description: 自定义事件监听器*/
public class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());System.out.println("消息:" + event.getId() + ":" + event.getMessage());}
}
  • 这是一个用于监听 CustomEvent 事件的监听器,这里可以处理自定义操作。

4.1.3 自定义事件刷新监听器

CustomRefreshedEventListener.java

package com.lino.springframework.test.event;import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.context.event.ContextRefreshedEvent;/*** @description: 自定义事件刷新监听器* @author: lingjian* @createDate: 2022/12/1 15:48*/
public class CustomRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {System.out.println("刷新事件:" + this.getClass().getName());}
}

4.1.4 自定义事件关闭监听器

CustomClosedEventListener.java

package com.lino.springframework.test.event;import com.lino.springframework.context.ApplicationListener;
import com.lino.springframework.context.event.ContextClosedEvent;/*** @description: 自定义事件关闭监听器*/
public class CustomClosedEventListener implements ApplicationListener<ContextClosedEvent> {@Overridepublic void onApplicationEvent(ContextClosedEvent event) {System.out.println("关闭事件:" + this.getClass().getName());}
}

4.1.5 配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans><bean class="com.lino.springframework.test.event.CustomEventListener"/><bean class="com.lino.springframework.test.event.CustomRefreshedEventListener"/><bean class="com.lino.springframework.test.event.CustomClosedEventListener"/>
</beans>
  • spring.xml 配置文件中,添加了三个事件监听器,监听刷新、监控自定义事件、监听关闭事件。

4.2 单元测试

ApiTest.java

@Test
public void test_event() {// 1.初始化 BeanFactoryClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功监听到了"));applicationContext.registerShutdownHook();
}
  • 通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并传了相应的参数信息。

测试结果

刷新事件:com.lino.springframework.test.event.CustomRefreshedEventListener$$EnhancerByCGLIB$$b3c3d48f
收到:com.lino.springframework.context.support.ClassPathXmlApplicationContext@4cdbe50f消息;时间:Thu Dec 01 17:07:43 CST 2022
消息:1019129009086763:成功监听到了
关闭事件:com.lino.springframework.test.event.CustomClosedEventListener$$EnhancerByCGLIB$$20578a33
  • 从测试结果看,我们自定义的事件和监听,以及监听系统的事件信息,都可以在控制台完成的输出。

五、总结:容器事件和事件监听器

  • 在现有的 Spring 的实现过程中,可以逐步看到很多设计模式的使用。
    • 比如:简单工厂 BeanFactory、工厂方法 FactoryBean、策略模式访问资源、观察者模式 Event
  • 本节关于观察者模式的实现过程,主要包括了事件的定义、事件的监听和发布事件,发布完成后根据匹配策略,监听器就会收到属于自己的事件内容,并做相应的处理动作。在结合 Spring 后,可以看到如何把观察者的实现和应用上下文结合。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/101506.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频导出文件太大如何变小?缩小视频这样做

作为一名视频制作爱好者&#xff0c;我们经常需要导出视频文件&#xff0c;但是&#xff0c;有时候我们会发现导出的视频文件太大&#xff0c;给上传和分享带来很大的不便。那么&#xff0c;如何将视频文件变小呢&#xff1f;下面将为你介绍三个方法&#xff0c;让你轻松解决视…

20个经典巧妙电路合集

1、防反接保护&#xff08;二极管&#xff09; 在实际电子设计中&#xff0c;防反接保护电路非常重要&#xff0c;不要觉得自己肯定不会接错&#xff0c;实际上无论多么小心&#xff0c;还是会犯错误...... 最简单的就是利用二极管了&#xff0c;利用二极管的单向导电性&#…

ESP32用作经典蓝牙串口透传模块与手机进行串口通信

ESP32用作经典蓝牙串口透传模块与手机进行串口通信 简介ESP32开发板Arduino程序手机与ESP32开发板进行蓝牙串口透传通信总结 简介 ESP32-WROOM-32模组集成了双模蓝牙包括传统蓝牙&#xff08;BR/EDR&#xff09;、低功耗蓝牙&#xff08;BLE&#xff09;和 Wi-Fi&#xff0c;具…

核心实验12合集_vlan高级配置:基于子网划分vlan超级vlan相同vlan 端口隔离 _ENSP

项目场景一&#xff1a; 核心实验12合集-1_vlan高级配置_ENSP 基于子网划分vlan &#xff11; 当检测ip在192.168.10.0/24时候&#xff0c;PC接入交换机时&#xff0c;将其划为vlan10&#xff0c; 且可以和vlan 10 的服务器通信。 2 当检测ip在192.168.20.0/24时候&#xff0c;…

设备管理系统有什么功能?它有什么用?

设备管理系统已成为现代化大规模研究所&#xff0c;信息化管理体系建设中最为关键的要素。随着工业设备的机械化、自动化、大型化、高速化以及复杂化等因素不断叠加&#xff0c;设备设施对于工业生产的作用和影响越来越大&#xff0c;其各项制度和流程也涉及面广、内容繁杂。  …

rrweb入门

rrweb 背景 rrweb 是 record and replay the web&#xff0c;是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式&#xff08;如 WebRTC&#xff09;不同的是&#xff0c;rrweb 录制的不是真正的视频流&#xff0c;而是一个记录页面 DOM 变化的 JSON 数组&#x…

Ab3d.DXEngine 6.0 Crack 2023

Ab3d.DXEngine 不是另一个游戏引擎&#xff08;如Unity&#xff09;&#xff0c;它强迫您使用其游戏编辑器、其架构&#xff0c;并且需要许多技巧和窍门才能在标准 .Net 应用程序中使用。Ab3d.DXEngine 是一个新的渲染引擎&#xff0c;它是从头开始构建的&#xff0c;旨在用于标…

mysql之DML的select分组排序

目录 一、创建表employee和department表 1.创建department表 2.创建employee表 3.给employee表格和department表格建立外键 4.给department插入数据 5.给employee表插入数据 6.删除名字为那个的数据 二、分组查询和排序查询&#xff0c;以及对数据的处理&#xff08;av…

controller接口上带@PreAuthorize的注解如何访问 (postman请求示例)

1. 访问接口 /*** 查询时段列表*/RateLimiter(time 10,count 10)ApiOperation("查询时段列表")PreAuthorize("ss.hasPermi(ls/sy:time:list)")GetMapping("/list")public TableDataInfo list(LsTime lsTime){startPage();List<LsTime> l…

ARM DIY(九)陀螺仪调试

前言 今天调试六轴陀螺仪 MPU6050 硬件 硬件很简单&#xff0c;使用 I2C 接口&#xff0c;并且没有使用中断引脚。 焊接上 MPU6050 芯片和上拉电阻、滤波电容。 检测 MPU6050 是挂在 I2C-0 上的&#xff0c;I2C-0 控制器的驱动已 OK&#xff0c;所以直接使用 I2C-0 检测 …

神经网络中的一些优化器整理

6 梯度平方的指数移动平均在神经网络优化中具有以下好处&#xff1a; 自适应学习率&#xff1a;梯度平方的指数移动平均允许每个参数的学习率自适应地调整。如果某个参数的梯度平方历史信息较大&#xff0c;那么其指数移动平均值会较大&#xff0c;从而减小学习率&#xff0c;使…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 &#x1f984;条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions&#x1f680;实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如&#xff0c;PHP 的本机会话处理程序模块…