手写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.rhkb.cn/news/122873.html

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

相关文章

如何将枯燥的大数据进行可视化处理?

在数字时代&#xff0c;大数据已经成为商业、科学、政府和日常生活中不可或缺的一部分。然而&#xff0c;大数据本身往往是枯燥的、难以理解的数字和文字&#xff0c;如果没有有效的方式将其可视化&#xff0c;就会错失其中的宝贵信息。以下是一些方法&#xff0c;可以将枯燥的…

基于SpringBoot的汽车租赁系统

基于SpringBootVue的汽车租赁系统&#xff0c;前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 角色&#xff1a;管理员、业务员、用户 管理员 用户管理…

STM32 软件IIC 控制OLED 显示屏

1. 硬件IIC 实在是太难用了&#xff0c;各种卡死&#xff0c;各种发不出来数据&#xff0c;没那么多时间折腾了&#xff0c;还是用软件IIC 先吧&#xff0c;初始化 void OLED_Software_IIC_Init(void) {GPIO_InitTypeDef GPIO_InitStruct;RCC_AHBPeriphClockCmd(OLED_SOFTWARE…

Jmeter进阶使用指南-使用断言

Apache JMeter是一个流行的开源负载和性能测试工具。在JMeter中&#xff0c;断言&#xff08;Assertions&#xff09;是用来验证响应数据是否符合预期的一个重要组件。它是对请求响应的一种检查&#xff0c;如果响应不符合预期&#xff0c;那么断言会标记为失败。 以下是如何在…

Linux如何安装MySQL

Linux安装MySQL5.7 1、下载 官网下载地址&#xff1a;http://dev.mysql.com/downloads/mysql/ 2、复制下面几个文件 3、检查当前系统是否安装过mysql、检查当前mysql依赖环境、检查/tmp文件夹权限 1&#xff09;检查当前系统是否安装过mysql&#xff0c;执行安装命令前&am…

LabVIEW应用开发——LabVIEW2019保姆级介绍、安装、第一个程序

一、前言 LabVIEW是一种程序开发环境&#xff0c;由美国国家仪器&#xff08;NI&#xff09;公司研制开发&#xff0c;类似于C和BASIC开发环境&#xff0c;但是LabVIEW与其他计算机语言的显著区别是&#xff1a;其他计算机语言都是采用基于文本的语言产生代码&#xff0c;而Lab…

MyBatis配置及单表操作

文章目录 一. MyBatis概述二. MyBatis项目的创建1. 准备一个数据表2. 创建项目 三. MyBatis的使用1. 基本使用2. SpringBoot单元测试 四. 使用MyBatis实现单表操作1. 查询2. 修改3. 删除4. 新增 五. 基于注解完成SQL 一. MyBatis概述 MyBatis 是一款优秀的持久层框架&#xff…

Ubutnu允许ssh连接使用root与密码登录

文章目录 1. 修改sshd_config2. 设置root密码3. 重启SSH服务 1. 修改sshd_config 修改/etc/ssh/sshd_config文件&#xff0c;找到 #Authentication&#xff0c;将 PermitRootLogin 参数修改为 yes。如果 PermitRootLogin 参数被注释&#xff0c;请去掉首行的注释符号&#xff…

GitHubGiteeGitlab极狐(JihuLab)配置SSH公私钥详细过程

GitHub-微软-github.com Gitee-开源中国- gitee.com Gitlab-乌克兰GitLab 公司-gitlab.com 极狐(JihuLab)-中国代理商运营的Gitlab -gitlab.cn或者jihulab.com 一、生成SSH公钥和私钥 1.1 取消全局设置 $ git config --global user.name "你的名字" $ git confi…

创建10个线程并发执行(STL/Windows/Linux)

C并发编程入门 目录 STL 写法 #include <thread> #include <iostream> using namespace std;void thread_fun(int arg) {cout << "one STL thread " << arg << " !" << endl; }int main(void) {int thread_count 1…

线性代数的学习和整理19,特征值,特征向量,以及引入的正交化矩阵概念

目录 1 什么是特征值和特征向量&#xff1f; 1.1 特征值和特征向量这2个概念先放后 1.2 直观定义 1.3 严格定义 2 如何求特征值和特征向量 2.1 方法1&#xff1a;结合图形看&#xff0c;直观方法求 2.1.1 单位矩阵的特征值和特征向量 2.1.2 旋转矩阵 2.2 根据严格定义…

Typroa+PicGo验证图片上传选项失败原因:测试图片被封禁

使用环境 Windows 11PicGo 2.3.1Typora 1.2.5图床 sm.ms 问题描述 在搭建好环境之后&#xff0c;在Typroa里面验证图片上传选项&#xff0c;显示失败。 查看日志 2023-09-06 09:51:23 [PicGo INFO] [PicGo Server] is listening at 36677 2023-09-06 09:51:28 [PicGo INF…

OpenCV之ellipse函数

ellipse函数用来在图片中绘制椭圆、扇形&#xff0c;有两个重载函数。 函数原型1&#xff1a; void cv::ellipse( InputOutputArray img,Point center,Size axes,double angle,double startAngle,double …

linux 下 C++ 与三菱PLC 通过MC Qna3E 二进制 协议进行交互

西门子plc 有snap7库 进行交互&#xff0c;并且支持c 而且跨平台。但是三菱系列PLC并没有现成的开源项目&#xff0c;没办法只能自己拼接&#xff0c;我这里实现了MC 协议 Qna3E 帧&#xff0c;并使用二进制进行交互。 #pragma once#include <stdio.h> #include <std…

ChatGPT Prompting开发实战(五)

一、如何编写有效的prompt 对于大语言模型来说&#xff0c;编写出有效的prompt能够帮助模型更好地理解用户的意图(intents)&#xff0c;生成针对用户提问来说是有效的答案&#xff0c;避免用户与模型之间来来回回对话多次但是用户不能从LLM那里得到有意义的反馈。本文通过具体…

数据分享|R语言分析上海空气质量指数数据:kmean聚类、层次聚类、时间序列分析:arima模型、指数平滑法...

全文链接&#xff1a;http://tecdat.cn/?p30131 最近我们被客户要求撰写关于上海空气质量指数的研究报告。本文向大家介绍R语言对上海PM2.5等空气质量数据&#xff08;查看文末了解数据免费获取方式&#xff09;间的相关分析和预测分析&#xff0c;主要内容包括其使用实例&…

Redis从基础到进阶篇(四)----性能调优、分布式锁与缓存问题

目录 一、Redis 集群演变 1.1 ReplicationSentinel*高可用 1.2 ProxyReplicationSentinel(仅仅了解) 1.3 Redis Cluster 集群 (重点&#xff09; 1.3.1 Redis-cluster架构图 1.3.2 工作原理 1.3.3 主从切换 1.3.4 副本漂移 1.3.5 分片漂移 二、Redis版本历史&#xf…

Docker部署RabbitMQ

Docker部署RabbitMQ 介绍 RabbitMQ是一个开源的消息队列系统&#xff0c;它被设计用于在应用程序之间传递消息。它采用了AMQP&#xff08;高级消息队列协议&#xff09;作为底层通信协议&#xff0c;这使得它能够在不同的应用程序之间进行可靠的消息传递。 那么&#xff0c;…

JVM----GC(垃圾回收)详解

一、Automatic Garbage Collection&#xff08;垃圾回收&#xff09;简介 Automatic Garbage Collection &#xff08;自动垃圾回收&#xff09;是JVM的一个特性&#xff0c;JVM会启动相关的线程&#xff0c;该线程会轮训检查heap memeory&#xff0c;并确定哪些是未被引用的(…

保姆级教程——VSCode如何在Mac上配置C++的运行环境

vscode官方下载&#xff1a; 点击官网链接&#xff0c;下载对应的pkg&#xff0c;安装打开&#xff1b; https://code.visualstudio.com/插件安装 点击箭头所指插件商店按钮&#xff0c;yyds&#xff1b; 下载C/C 插件&#xff1b; ![外链图片转存 下载CodeLLDB插件&#x…