栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于spring实现事件监听

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于spring实现事件监听

目录
  • 事件监听
  • JDK 事件监听机制
    • 基于JDK实现事件监听
  • Spring 事件监听
    • 基于Spring 实现事件监听
    • 基于Spring 实现异步事件监听

事件监听

​ 常见事件监听机制的主要角色如下:
​ 事件及事件源:事件源发生某事件是特定事件监听器被触发的原因;
​ 事件监听器:监听器监听特定事件,并在内部定义了事件发生后的响应逻辑;
​ 事件发布器:事件监听器的容器,对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。

JDK 事件监听机制

​ JDK为提供了一个代表所有可被监听事件的事件基类 java.util.EventObject,所有自定义事件类型都必须继承该类:

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    
    protected transient Object  source;

    
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }

    
    public Object getSource() {
        return source;
    }

    
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

​ JDK 还提供了一个对所有事件监听器进行抽象的接口 java.util.EventListener,这是一个标记接口,所有自定义事件监听器都必须实现该标记接口:

public interface EventListener {

}

基于JDK实现事件监听

​ 以监听任务执行完毕,发送邮件为例:

​ 1、定义事件源

@Data
@AllArgsConstructor
public class Task {

    
    private String name;

    
    private Integer code;

}

​ 2、定义事件

@Getter
public class TaskEvent extends EventObject {

    
    private String message;

    
    private String receiver;


    
    public TaskEvent(Object source, String message, String receiver) {
        super(source);
        this.message = message;
        this.receiver = receiver;
    }
}

​ 3、定义事件监听器

public interface TaskEventListener extends EventListener {

    
    void handleTask(TaskEvent event);
}



@Slf4j
@Data
public class MailTaskListener implements TaskEventListener {
    private static final Integer OK = 200;

    @Override
    public void handleTask(TaskEvent event) {
        Task source = (Task)event.getSource();

        if(Objects.equals(source.getCode(), OK)){
            
            log.info("尊敬的{},您好!任务-{}已执行完毕!{}", event.getReceiver(), source.getName(), event.getMessage());
        }
    }
}

​ 4、定义事件发布器

public class TaskEventPublisher {

    
    private List listeners = new ArrayList<>();


   
    public synchronized void register(TaskEventListener listener){
        if(!listeners.contains(listener)){
            listeners.add(listener);
        }
    }


    
    public synchronized boolean unregister(TaskEventListener listener){
        if(listeners.isEmpty()){
            return true;
        }
        return listeners.remove(listener);
    }


    
    public void publishEvent(TaskEvent event){
        // 循环调用
        listeners.stream().forEach(listener -> listener.handleTask(event));
    }

}

​ 5、测试

 public static void main(String[] args) {

        
        Task task = new Task("订单10001开始发货!", 200);

        
        TaskEvent event = new TaskEvent(task, "回复TD退订", "基地");

        
        MailTaskListener listener = new MailTaskListener();

        
        TaskEventPublisher publisher = new TaskEventPublisher();

        
        publisher.register(listener);

        
        publisher.publishEvent(event);
    }

​ 最终测试结果如下:

Spring 事件监听

​ Spring 框架对事件的发布与监听提供了相对完整的支持,它扩展了JDK中对自定义事件监听提供的基础框架(java.util.EventObject 和 java.util.EventListener),并与Spring的IOC特性作了整合,使得用户可以根据自己的业务特点进行相关的自定义,并依托Spring容器方便的实现监听器的注册和事件的发布。

​ Spring 事件机制有几个重要的接口:
​ 1、ApplicationEvent,Spring 提供的事件抽象类,继承自 java.util.EventObject;
​ 2、ApplicationListener,Spring 提供的事件监听器接口,继承自 java.util.EventListener,提供了一个 onApplicationEvent 方法,用于执行具体的事件处理逻辑;
​ 3、ApplicationEventPublisher,Spring 提供的事件发布接口,ApplicationContext 实现了该接口,该接口提供了一个publishEvent 方法,用来发布事件;
​ 4、ApplicationEventMulticaster,Spring 事件机制中的事件广播器,默认实现 SimpleApplicationEventMulticaster,该组件会在容器启动时被自动创建,并以单例的形式存在,管理了所有的事件监听器,并提供针对所有容器内事件的发布功能。

执行流程

​ Spring 事件执行流程为:当一个事件源产生事件时,它经过事件发布器 ApplicationEventPublisher 发布事件,而后事件广播器ApplicationEventMulticaster 会去事件注册表 ApplicationContext 中找到事件监听器 ApplicationListnener,而且逐个执行监听器的 onApplicationEvent 方法,从而完成事件监听器的逻辑。

​Spring 提供5种标准的事件监听:
​ 1、ContextRefreshedEvent:上下文更新事件,会在 ApplicationContext 被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的 refresh() 方法时被触发;
​ 2、ContextStartedEvent:上下文开始事件,当容器 ConfigurableApplicationContext 的 start() 方法开始/重新开始容器时触发该事件;
​ 3、ContextStoppedEvent:上下文停止事件,当容器 ConfigurableApplicationContext 的 stop() 方法停止容器时触发该事件;
​ 4、ContextClosedEvent:上下文关闭事件,当 ApplicationContext 被关闭时触发该事件;
​ 5、RequestHandledEvent:请求处理事件,在web应用中,当一个http请求结束会触发该事件。

Spring Boot扩展了 Spring的 ApplicationContextEvent,提供了四种事件:
​ 1、ApplicationStartedEvent: spring boot启动开始时触发;
​ 2、ApplicationEnvironmentPreparedEvent:spring boot 对应 Enviroment已经准备完毕会触发,此时上下文还没创建;
​ 3、ApplicationPreparedEvent:spring boot上下文 context 创建完成触发,但此时 spring 中的 bean 是没有完全加载完成的;
​ 4、ApplicationFailedEvent:spring boot 启动异常时触发。

基于Spring 实现事件监听

自定义 Spring 事件监听的流程分4步:
​ 1、自定义事件,继承 ApplicationEvent 抽象类;
​ 2、定义事件监听器:
​       a、实现接口 ApplicationListener,重写 onApplicationEvent 方法来自定义相关事件的处理逻辑;
​       b、在某个方法上使用 @EventListener注解即可。
​ 3、注册监听器:
​       a、通过 SpringApplication 的 addListeners 方法,手动注册;
​       b、通过注解 @Component 将监听器加入到容器中;
​       c、在配置文件中通过 context.listener.classes 指定监听器。
​ 4、发布事件,触发监听。使用实现了 ApplicationEventPublisher 接口的类(常用 ApplicationContext)的publishEvent(ApplicationEvent event)方法发布事件。

自定义事件

​ 通过继承 ApplicationEvent 来自定义事件:

@Getter
@Setter
public class MessageEvent extends ApplicationEvent implements Serializable {
    private static final long serialVersionUID = 3529299915023264729L;

    
    private Integer type;

    
    private T content;

    public MessageEvent(Object source, Integer type, T content) {
        super(source);
        this.type = type;
        this.content = content;
    }
}

​ 注意:一定要显示创建默认的构造方法,构造器的参数为该事件的相关数据对象,监听器可以获取到该数据对象,进而进行相关逻辑处理。

定义事件监听器

​ 方式一,实现接口 ApplicationListener,重写 onApplicationEvent 方法:

@Slf4j
public class MessageEventListenter implements ApplicationListener> {

    @Override
    public void onApplicationEvent(MessageEvent event) {
        Integer type = event.getType();
        T content = event.getContent();

        // 发短信
        if(type == 1){
            log.info("模拟发送短信,短信内容为:{}", content);
        }
        // 发邮件
        else if(type == 2){
            log.info("模拟发送邮件,邮件内容为:{}", content);
        }
        // 站内信
        else {
            log.info("模拟发送站内信,站内信内容为:{}", content);
        }
    }
}

​ 方式二,使用注解@EventListener:

@Slf4j
public class MessageEventHandler {


    
    @EventListener(classes = MessageEvent.class)
    public void handlerMessageEvent(MessageEvent event){
        Integer type = event.getType();
        T content = event.getContent();

        // 发短信
        if(type == 2){
            log.info("模拟发送短信,短信内容为:{}", content);
        }
        // 发邮件
        else if(type == 1){
            log.info("模拟发送邮件,邮件内容为:{}", content);
        }
        // 站内信
        else {
            log.info("模拟发送站内信,站内信内容为:{}", content);
        }
    }

}

注册监听器

​ 注册监听器,其实质就是将监听器进行IOC处理,让 spring 容器管理监听器的生命周期。 在SpringBoot中有三种方式可以将自定义的监听器纳入spring容器管理:

具体实现备注
在SpringBoot启动类的main方法中,使用 SpringApplication 的 addListeners 方法进行注册进行单元测试时,(由于不会走SpringBoot启动的类main方法,所以)此方式不生效。
也不能搭配 @Async 注解,进行异步监听。
通过 @Component 或类似注解,将监听器(或监听器方法所在的)纳入容器管理推荐使用方式,能够搭配@Async 注解,进行异步监听。
在SpringBoot配置文件中指定监听器(或监听器方法所在的)类多个监听器,使用逗号分割即可。
不能搭配 @Async 注解,进行异步监听。

​ 方式一,使用 SpringApplication 的 addListeners 方法进行注册:

 public static void main(String[] args) {
        SpringApplication application =	new SpringApplication(AnswerAdminApplication.class);
     	// 添加监听器
        application.addListeners(new MessageEventListenter());
        application.run(args);
    }

​ 方式二,使用注解将监听器纳入容器管理:

@Component
@Slf4j
public class MessageEventHandler {


    
    @EventListener(classes = MessageEvent.class)
    public void handlerMessageEvent(MessageEvent event){
        Integer type = event.getType();
        T content = event.getContent();

        // 发短信
        if(type == 2){
            log.info("模拟发送短信,短信内容为:{}", content);
        }
        // 发邮件
        else if(type == 1){
            log.info("模拟发送邮件,邮件内容为:{}", content);
        }
        // 站内信
        else {
            log.info("模拟发送站内信,站内信内容为:{}", content);
        }
    }
}



@Component
@Slf4j
public class MessageEventListenter implements ApplicationListener> {

    @Override
    public void onApplicationEvent(MessageEvent event) {
        Integer type = event.getType();
        T content = event.getContent();

        // 发短信
        if(type == 1){
            log.info("模拟发送短信,短信内容为:{}", content);
        }
        // 发邮件
        else if(type == 2){
            log.info("模拟发送邮件,邮件内容为:{}", content);
        }
        // 站内信
        else {
            log.info("模拟发送站内信,站内信内容为:{}", content);
        }
    }
}

​ 方式三,使用配置文件设置监听器

# 配置监听器
context.listener.classes: com.vvupup.answer.admin.event.MessageEventListenter

事件发布

​ 调用 ApplicationEventPublisher 接口的 publishEvent 方法,发布事件:

@RestController
@RequestMapping("/event")
public class TestController {

    @Autowired
    private ApplicationContext applicationContext;

    @GetMapping("/pub")
    public String test(){
        
        applicationContext.publishEvent(new MessageEvent<>(this, 1, "短信"));
        return "ok";
    }
}

​ 最终执行结果:

基于Spring 实现异步事件监听

​ 上面的例子实现事件监听使用的是同步方式,还可以结合注解 @Async 实现异步事件监听:

@Configuration
@EnableAsync   // @EnableAsync 注解开启异步
public class AsyncConfig extends AsyncConfigurerSupport {

    
    @Bean("executor")
    public Executor executor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 线程池核心线程数量
        executor.setCorePoolSize(5);
        // 队列最大长度
        executor.setQueueCapacity(200);
        // 最大线程数量
        executor.setMaxPoolSize(10);
        // 线程池拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 线程名前缀
        executor.setThreadNamePrefix("async-");
        // 允许空闲时间
        executor.setKeepAliveSeconds(30);
        // 初始化
        executor.initialize();
        return executor;
    }


    
    @Override
    public Executor getAsyncExecutor() {
        return executor();
    }
}





@Data
@Slf4j
public class MessageEventListener implements ApplicationListener> {


    
    @Async(value = "executor")
    @Override
    public void onApplicationEvent(MessageEvent event) {

        Integer type = event.getType();
        T content = event.getContent();

        // 发短信
        if(type == 1){
            log.info("模拟发送短信,短信内容为:{}", content);
        }
        // 发邮件
        else if(type == 2){
            log.info("模拟发送邮件,邮件内容为:{}", content);
        }
        // 站内信
        else {
            log.info("模拟发送站内信,站内信内容为:{}", content);
        }
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/630948.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号