手写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.mzph.cn/news/73406.shtml

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

相关文章

汇川PLC学习Day1:跑马灯程序编写

汇川PLC学习Day1&#xff1a;跑马灯程序编写 一、 软件安装 进入官网下载软件 二、 使用帮助 三、 新建工程与功能代码实现 CtrlN 寻找内带输出模块的CPU并设置好工程名字与保存路径&#xff0c;语言选择想熟悉的类型 工程建立后&#xff0c;PLC_PRG即为用户编写程序文件…

如何优化网站SEO(提高排名和流量的3个小知识)

百度百科SEO简介&#xff1a;搜索引擎优化&#xff08;SearchEngineOptimization&#xff09;是指通过对网站内部结构、外部链接以及页面内容等进行调整&#xff0c;从而使其在搜索引擎中排名更靠前&#xff0c;从而带来更多的流量和曝光。SEO是数字营销中的重要一环&#xff0…

合并区间【贪心算法】

合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 class Solution {public int[][] merge(int[…

TCP Header都有啥?

分析&回答 源端口号&#xff08;Source Port&#xff09; &#xff1a;16位&#xff0c;标识主机上发起传送的应用程序&#xff1b; 目的端口&#xff08;Destonation Port&#xff09; &#xff1a;16位&#xff0c;标识主机上传送要到达的应用程序。 源端&#xff0c;目…

安全测试 —— 你了解WEB安全测试吗?

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

Docker镜像解析获取Dockerfile文件

01、概述 当涉及到容器镜像的安全时&#xff0c;特别是在出现镜像投毒引发的安全事件时&#xff0c;追溯镜像的来源和解析Dockerfile文件是应急事件处理的关键步骤。在这篇博客中&#xff0c;我们将探讨如何从镜像解析获取Dockerfile文件&#xff0c;这对容器安全至关重要。 02…

【数学建模】2023数学建模国赛C题完整思路和代码解析

C题第一问代码和求解结果已完成&#xff0c;第一问数据量有点大&#xff0c;经过编程整理出来了单品销售额的汇总数据、将附件2中的单品编码替换为分类编码&#xff0c;整理出了蔬菜各品类随着时间变化的销售量&#xff0c;并做出了这些疏菜品类的皮尔森相关系数的热力图&#…

全球城市汇总【最新】

文章目录 案例图国家城市大洲 数据获取政策&#xff1a; 全球城市、国家、介绍汇总。包含 .csv .sql .xml 格式数据。 案例图 国家 城市 大洲 数据 获取上图资源绑定 https://blog.csdn.net/qq_40374604/category_12435042.html 获取政策&#xff1a; 如找不到在合集中查找…

PPT架构师架构技能图

PPT架构师架构技能图 目录概述需求&#xff1a; 设计思路实现思路分析1.软素质2.核心输出&#xff08;office输出&#xff09; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,ma…

printf scanf

目录 printf scanf printf 把十的二进制代码放进去了&#xff0c;i对的是二进制代码&#xff0c;指定这一串0101代码以什么样的格式输出。 为什么要输出控制符&#xff0c;因为里面放的是二进制&#xff0c;必须控制输出的格式&#xff0c;指定这一串二进制以什么样的格式输出…

跨境电商产业链,服务商的“霸道”你见识过吗?(测评补单)

跨境电商行业的服务商众多&#xff0c;涉及到从前期培训和店铺注册准备到中期选品软件、营销服务、流量投放和支付等多个环节。然而&#xff0c;行业乱象也日益严重&#xff0c;出现了一些不良现象&#xff0c;如恶意竞争、高价要求、割韭菜等。 卖家在选择服务商时应谨慎&…

ZFS了解

存储数据的管理通常涉及两个方面:对一个或多个块存储设备(如硬盘驱动器和SD卡)进行物理卷管理&#xff0c;并将它们组织成操作系统所看到的逻辑块设备(通常涉及卷管理器、RAID控制器、阵列管理器或合适的设备驱动程序)&#xff0c;以及对存储在这些逻辑块设备(文件系统或其他数…

SpringBoot 统一功能处理

目录 一、用户登录权限验证 1.1 SpringAOP可以进行处理吗&#xff1f; 1.2 创建自定义拦截器 1.3 将自定义拦截器配置到系统配置项中 1.4 拦截器的实现原理 1.4.1 实现原理源码分析 1.5 统一访问前缀添加 二、统一异常处理 2.1 为什么需要使用统一异常处理&#xff1f;…

基于antd+vue2来实现一个简单的绘画流程图功能

简单流程图的实现&#xff08;基于antdvue2的&#xff09;代码很多哦~ 实现页面如下 1.简单操作如下 2.弹框中使用组件&#xff1a; <vfdref"vfd"style"background-color: white;":needShow"true":fieldNames"fieldNames"openUse…

day-04 基于UDP的服务器端/客户端

一.理解UDP &#xff08;一&#xff09;UDP套接字的特点 UDP套接字具有以下特点&#xff1a; 无连接性&#xff1a;UDP是一种无连接的协议&#xff0c;这意味着在发送数据之前&#xff0c;不需要在发送方和接收方之间建立连接。每个UDP数据包都是独立的&#xff0c;它们可以独…

FOXBORO FBM232 P0926GW 自动化控制模块

Foxboro FBM232 P0926GW 是 Foxboro&#xff08;福克斯博罗&#xff09;自动化控制系统的一部分&#xff0c;通常用于监测和控制工业过程。以下是关于这种类型的自动化控制模块可能具有的一些常见功能&#xff1a; 数字输入通道&#xff1a; FBM232 P0926GW 控制模块通常具有多…

【漏洞库】Fastjson_1.2.24_rce

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- 利用工具- 漏洞环境- 漏洞扫描- 漏洞验证- 深度利用- GetShell- EXP 编写 漏洞挖掘- 指纹信息 修复建议- 漏洞修复 漏洞原理 漏洞描述 Fastjson 存在反序列化远程代码执行漏洞&#xff0c;当应用或系统使用 Fastjson 对由…

十一、做高并发内存池项目过程中遇到的bug以及调试bug的方法和心得

十一、做高并发内存池项目过程中遇到的bug以及调试bug的方法和心得 第一个bug是内存问题&#xff0c;程序直接崩溃&#xff0c;问题出现在&#xff1a;GetOneSpan函数中的切分span的时候结尾的span1的next没有置空。 第二个bug是还小内存块给span的时候找不到小内存所属的spa…

使用Android原生制作毛玻璃效果图片

毛玻璃效果&#xff0c;也被称为模糊效果&#xff0c;是许多现代应用中流行的一种视觉效果。在 Android 中&#xff0c;我们可以通过多种方式实现该效果。本文将探讨如何使用 Android 原生的 Bitmap 类和 RenderScript 来实现毛玻璃效果。 1. 准备工作 首先&#xff0c;你需要…

BLE Mesh蓝牙mesh网多跳大数据量高带宽传输数据方法

1、BLE Mesh数据传输现状 BLE Mesh网络技术是低功耗蓝牙的一个进阶版&#xff0c;Mesh扩大了蓝牙在应用中的规模和范围&#xff0c;因为它同时支持超过三万个网络节点&#xff0c;可以跨越大型建筑物&#xff0c;不仅可以使得医疗健康应用更加方便快捷&#xff0c;还能监测像学…