聊聊Spring事件及其应用

简介: 在 JDK 中已经提供相应的自定义事件发布功能的基础类:- `java.util.EventObject`类 :自定义**事件**类型- `java.util.EventListener`接口:事件的**监听器**

在 JDK 中已经提供相应的自定义事件发布功能的基础类:

  • java.util.EventObject类 :自定义事件类型
  • java.util.EventListener接口:事件的监听器

首先了解几个概念:

img

Spring 事件类结构

img

1. 事件类

事件类也就是定义发送的内容,比如可以通过继承ApplicationContextEvent来自定义一个特定事件类。

img

1.1 ApplicationEvent

首先是继承 EventObjectApplicationEvent,通过source来指定事件源:

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);
    }
}

1.2 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();
    }

}

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);
    }

}

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);
    }

}

我们可以通过继承该类来实现,特定的事件类型需求,比如要实现一个邮件发送事件。只需要继承ApplicationContextEvent即可:

public class MailSendEvent extends ApplicationContextEvent {
   
   
    private String msg;

    public MailSendEvent(Object source, String msg) {
   
   
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
   
   
        return msg;
    }

    public void setMsg(String msg) {
   
   
        this.msg = msg;
    }
}

同时ApplicationContextEvent也有特定的几个子类,来表示容器启动、刷新、停止以及关闭事件:

img

2.事件监听器

事件监听器接口中,只定义了一个方法:onApplicationEvent(E event)该方法接收ApplicationEvent事件对象,在该方法中编写事件的响应处理逻辑。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
   
   

    /**
     * 接收ApplicationEvent 事件对象
     * 在该方法中编写事件的响应处理逻辑
     * @param event
     */
    void onApplicationEvent(E event);
}

我们同样也可以实现该接口来实现特定的事件监听器功能,比如邮件发送的监听器:

public class MailSenderListener implements ApplicationListener<MailSendEvent> {
   
   

    @Override
    public void onApplicationEvent(MailSendEvent event) {
   
   
        System.out.println("邮件发送器的 resource:" + event.getSource() + "邮件发送器的 msg:" + event.getMsg());
    }
}

3.事件广播器

事件广播器负责将事件通知监听器注册表中的事件监听器,然后再由事件监听器分别对事件进行响应。Spring中定义了如下接口:

img

public interface ApplicationEventMulticaster {
   
   

    /**
     * 添加事件监听器
     * @param listener
     */
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * 移除事件监听器
     * @param listener
     */
    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * 广播事件
     * @param event
     */
    void multicastEvent(ApplicationEvent event);
}

及其简单实现类SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{
   
   

    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
   
   
        setBeanFactory(beanFactory);
    }
    /**unchecked 表示告诉编译器忽略指定的警告,不用再编译完成后出现警告信息*/
    @SuppressWarnings("unchecked")
    @Override
    public void multicastEvent(ApplicationEvent event) {
   
   
        for (ApplicationListener applicationListener : getApplicationListeners(event)) {
   
   
            applicationListener.onApplicationEvent(event);
        }
    }
}

4.事件发布者

它本身作为事件源,会在合适的时点,将相应事件发布给对应的事件监听器:

public interface ApplicationEventPublisher {
   
   

    /**
     * 通知监听者并发布事件
     * @param event
     */
    void publishEvent(ApplicationEvent event);
}

在Spring容器事件中,ApplicationContext接口定义继承了ApplicationEventPublisher接口,所以实际上AbstractApplicationContext在事件中承担了事件发布者的角色。

但是在实际上具体实现事件的发布和事件监听器注册方面,将功能转接给ApplicationEventMulticaster接口,最终具体实现则放在AbstractApplicationEventMulticaster的实现类中:

img

Spring 事件类的应用

那么在Spring中,事件类到底是如何运行的呢?首先我们会在xml配置文件中配置相应的ApplicationListener类型的监听器,因此在容器启动后,这些类型的bean会被ApplicationContext容器所识别,它们负责监听容器内发布的对应的ApplicationEvent类型的事件。

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.ethan.springframework.test.event.MailSenderListener"/>
<bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>

AbstractApplicationContextrefresh()方法中可以看到自动注册的内容:

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));
}
public void publishEvent(ApplicationEvent event) {
   
   
    applicationEventMulticaster.multicastEvent(event);
}

所以在ApplicationContext容器启动时,会自动注册EventListener类型的 Bean,一旦检测到有ApplicationContextEvent类型的事件发布,将通知这些注册到容器的EventListener

应用实例

下面将构建一个发送邮件的Spring事件实例:

1. 邮件发送事件MailSendEvent

public class MailSendEvent extends ApplicationContextEvent {
   
   
    private String msg;

    public MailSendEvent(Object source, String msg) {
   
   
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
   
   
        return msg;
    }
}

2.邮件发送事件监听器MailSendListener(邮件发送事件)、ContextRefreshedEventListener(容器刷新事件) 和 ContextClosedEventListener(容器关闭事件)

public class MailSenderListener implements ApplicationListener<MailSendEvent> {
   
   

    @Override
    public void onApplicationEvent(MailSendEvent event) {
   
   
        System.out.println("邮件发送器的 resource:" + event.getSource() + "邮件发送器的 msg:" + event.getMsg());
    }
}
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
   
   

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
   
   
        System.out.println("关闭事件:" + this.getClass().getName());
    }
}
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
   
   

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
   
   
        System.out.println("刷新/打开事件:" + this.getClass().getName());
    }
}

这时,将监听器们注入xml文件中:

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.ethan.springframework.test.event.MailSenderListener"/>
<bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>

3.邮件发送事件发布者

事件发布者ApplicationEventPublisher,因为前面提到,applicationContext继承了ApplicationEventPublisher,而applicationContext将事件发布功能委托给了ApplicationEventMulticaster,容器在启动开始就会检查是否存在名称为applicationEventMulticasterApplicationEventMulticaster对象实例,如果有就使用提供的实现,没有则默认初始化一个SimpleApplicationEventMulticaster作为将会使用的ApplicationEventMulticaster

/**
 * @description: 实现了事件监听器的管理功能
 * @author: wjw
 * @date: 2022/7/9
 */
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 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 : allListeners) {
   
   
            if (supportsEvent(listener, event)) {
   
   
                allListeners.add(listener);
            }
        }
        return allListeners;
    }

    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
   
   
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        /**根据不同实例化类型,判断后获取对应目标 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);
        }

        return eventClassName.isAssignableFrom(event.getClass());
    }

}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{
   
   

    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
   
   
        setBeanFactory(beanFactory);
    }
    /**unchecked 表示告诉编译器忽略指定的警告,不用再编译完成后出现警告信息*/
    @SuppressWarnings("unchecked")
    @Override
    public void multicastEvent(ApplicationEvent event) {
   
   
        for (ApplicationListener applicationListener : getApplicationListeners(event)) {
   
   
            applicationListener.onApplicationEvent(event);
        }
    }
}

4.测试验证

public void test_event() {
   
   
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");

    applicationContext.publishEvent(new CustomEvent(applicationContext, 110L, "test!"));

    System.out.println("-----------------------------------------------------------------");
    applicationContext.publishEvent(new MailSendEvent(applicationContext, "邮件发送测试"));
    applicationContext.registerShutdownHook();
}
刷新/打开事件:cn.ethan.springframework.test.event.ContextRefreshedEventListener{
   
   mathJaxContainer[0]}2e5c458
-----------------------------------------------------------------
邮件发送器的 resource:cn.ethan.springframework.context.support.ClassPathXmlApplicationContext@5f2050f6邮件发送器的 msg:邮件发送测试
关闭事件:cn.ethan.springframework.test.event.ContextClosedEventListener{
   
   mathJaxContainer[1]}fbc2c978
目录
相关文章
|
1月前
|
安全 Java 网络安全
当网络安全成为数字生活的守护者:Spring Security,为您的应用筑起坚不可摧的防线
【9月更文挑战第2天】在数字化时代,网络安全至关重要。本文通过在线银行应用案例,详细介绍了Spring Security这一Java核心安全框架的核心功能及其配置方法。从身份验证、授权控制到防御常见攻击,Spring Security提供了全面的解决方案,确保应用安全。通过示例代码展示了如何配置`WebSecurityConfigurerAdapter`及`HttpSecurity`,帮助开发者有效保护应用免受安全威胁。
55 4
|
9天前
|
Java 对象存储 开发者
故障隔离与容错处理:Hystrix在Spring Cloud和Netflix OSS中的应用
故障隔离与容错处理:Hystrix在Spring Cloud和Netflix OSS中的应用
26 3
|
16天前
|
Kubernetes Cloud Native Java
当 Quarkus 遇上 Spring Boot,谁才是现代云原生应用的终极之选?究竟哪款能助你的应用傲视群雄?
Quarkus 和 Spring Boot 均为构建现代云原生应用的热门框架,旨在简化开发流程并提升性能。Spring Boot 依托庞大的 Spring 生态系统,提供开箱即用的体验,适合快速搭建应用。Quarkus 由红帽发起,专为 GraalVM 和 HotSpot 设计,强调性能优化和资源消耗最小化,是云原生环境的理想选择。
15 3
|
1月前
|
IDE Java 开发工具
还在为繁琐的配置头疼吗?一文教你如何用 Spring Boot 快速启动,让开发效率飙升,从此告别加班——打造你的首个轻量级应用!
【9月更文挑战第2天】Spring Boot 是一款基于 Spring 框架的简化开发工具包,采用“约定优于配置”的原则,帮助开发者快速创建独立的生产级应用程序。本文将指导您完成首个 Spring Boot 项目的搭建过程,包括环境配置、项目初始化、添加依赖、编写控制器及运行应用。首先需确保 JDK 版本不低于 8,并安装支持 Spring Boot 的现代 IDE,如 IntelliJ IDEA 或 Eclipse。
87 5
|
2月前
|
安全 NoSQL Java
JeecgBoot应用Spring Authorization Server
Spring Authorizaiton Server, 简称 sas,是一个授权服务器框架,提供 OAuth2.1 与 Open Connect 1.0 认证规范及其他规范的实现,它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 Identity Provider 和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础
38 2
|
2月前
|
Java Spring 监控
Spring Boot Actuator:守护你的应用心跳,让监控变得触手可及!
【8月更文挑战第31天】Spring Boot Actuator 是 Spring Boot 框架的核心模块之一,提供了生产就绪的特性,用于监控和管理 Spring Boot 应用程序。通过 Actuator,开发者可以轻松访问应用内部状态、执行健康检查、收集度量指标等。启用 Actuator 需在 `pom.xml` 中添加 `spring-boot-starter-actuator` 依赖,并通过配置文件调整端点暴露和安全性。Actuator 还支持与外部监控工具(如 Prometheus)集成,实现全面的应用性能监控。正确配置 Actuator 可显著提升应用的稳定性和安全性。
55 0
|
2月前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
39 0
|
2月前
|
Java Spring 供应链
Spring 框架事件发布与监听机制,如魔法风暴席卷软件世界,开启奇幻编程之旅!
【8月更文挑战第31天】《Spring框架中的事件发布与监听机制》介绍了Spring中如何利用事件发布与监听机制实现组件间的高效协作。这一机制像城市中的广播系统,事件发布者发送消息,监听器接收并响应。通过简单的示例代码,文章详细讲解了如何定义事件类、创建事件发布者与监听器,并确保组件间松散耦合,提升系统的可维护性和扩展性。掌握这一机制,如同拥有一把开启高效软件开发大门的钥匙。
39 0
|
2月前
|
Java Spring UED
Spring框架的异常处理秘籍:打造不败之身的应用!
【8月更文挑战第31天】在软件开发中,异常处理对应用的稳定性和健壮性至关重要。Spring框架提供了一套完善的异常处理机制,包括使用`@ExceptionHandler`注解和配置`@ControllerAdvice`。本文将详细介绍这两种方式,并通过示例代码展示其具体应用。`@ExceptionHandler`可用于控制器类中的方法,处理特定异常;而`@ControllerAdvice`则允许定义全局异常处理器,捕获多个控制器中的异常。
38 0
|
2月前
|
Java Spring 监控
危机时刻,Spring框架如何拯救你的应用?深入探讨健康检查与自我修复功能
【8月更文挑战第31天】在现代软件架构中,应用的稳定性和可用性至关重要。本文介绍Spring框架中的健康检查与自我修复机制,通过Spring Boot Actuator的`/health`端点监控应用状态,并结合Spring Cloud Hystrix实现服务容错和断路器功能,提高应用健壮性。借助这些工具,开发者能轻松监控应用健康状况并在发现问题时自动采取措施,确保服务高可用性。要实现完善的机制,需根据具体应用架构和需求进行配置和扩展。
40 0
下一篇
无影云桌面