Springboot之监听器

事件监听的几种方式

1 方式一:实现接口

场景:用户注册成功后,给用户赠送100元优惠券

1.1 创建事件

实现ApplicationEvent接口

package com.per.listener.e1;

import com.per.domain.UserDto;
import org.springframework.context.ApplicationEvent;

/**
 * @Title DemoEvent1
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public class UserEvent extends ApplicationEvent {

    /**
     * 事件源
     */
    private UserDto userDto;

    public UserEvent(UserDto userDto) {
        super(userDto);
        this.userDto = userDto;
    }

    /**
     * 获取事件中的用户信息
     *
     * @return
     */
    public UserDto getUserDto() {
        return userDto;
    }
}

1.2 创建事件监听器

实现ApplicationListener接口,重写onApplicationEvent方法

package com.per.listener.e1;

import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @Title UserListener
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Slf4j
@Component
public class UserListener implements ApplicationListener<UserEvent> {
    @Override
    public void onApplicationEvent(UserEvent event) {
        log.info("UserListener#onApplicationEvent 事件监听 开始执行...");
        UserDto userDto = event.getUserDto();
        // 给用户发优惠券
        log.info("给用户{}发送100元优惠卷", userDto.getName());
    }
}

1.3 发布事件

引入ApplicationContext,调用publishEvent方法发布事件

package com.per.service.impl;

import com.per.domain.UserDto;
import com.per.listener.e1.UserEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

/**
 * @Title UserServiceImpl
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public String userRegister(UserDto userDto) {
        log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());
        // 发送优惠券
        UserEvent userEvent = new UserEvent(userDto);
        applicationContext.publishEvent(userEvent);
        return String.format("用户%s注册成功,并赠送了100元优惠券", userDto.getName());
    }
}

2 方式二:注解方式

场景:用户注册成功后,给用户发送邮件、短信通知

2.1 创建事件

继承ApplicationEvent,重写事件构造方法

2.1.1 创建发送邮件事件
package com.per.listener.e2;

import org.springframework.context.ApplicationEvent;

/**
 * @Title SendMailEvent 发送邮件事件
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public class SendMailEvent extends ApplicationEvent {

    public SendMailEvent(Object source) {
        super(source);
    }

}
2.1.2 创建发送短信事件
package com.per.listener.e2;

import org.springframework.context.ApplicationEvent;

/**
 * @Title SendShortMsg 发送短信事件
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public class SendMsgEvent extends ApplicationEvent {

    public SendMsgEvent(Object source) {
        super(source);
    }
}

2.2 创建事件监听器

定义事件执行的方法,使用注解@EventListener标注方法,使用classes属性指定方法对应的事件

package com.per.listener.e2;

import com.per.domain.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Title UserE2Listener
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Component
@Slf4j
public class UserE2Listener {

    /**
     * 发送邮件
     * @param sendMailEvent 发送邮件事件
     */
    @EventListener(classes = SendMailEvent.class)
    @Order(1)
    public void sendMail(SendMailEvent sendMailEvent){
        UserDto userDto = (UserDto) sendMailEvent.getSource();
        log.info("发送邮件给用户{} ...【邮件】恭喜你注册成功", userDto.getName());
    }

    /**
     * 发送短信
     * @param sendMsgEvent 发送短信事件
     */
    @EventListener(classes = SendMsgEvent.class)
    @Order(2)
    public void sendMsg(SendMsgEvent sendMsgEvent){
        UserDto userDto = (UserDto) sendMsgEvent.getSource();
        log.info("发送短信给用户{} ...【短信】恭喜你注册成功", userDto.getName());
    }

}

2.3 发布事件

引入ApplicationEventPublisher,调用publishEvent方法发布事件

package com.per.service.impl;

import com.per.domain.UserDto;
import com.per.listener.e2.SendMailEvent;
import com.per.listener.e2.SendMsgEvent;
import com.per.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

/**
 * @Title UserServiceImpl
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private ApplicationEventPublisher publisher;

    @Override
    public String userRegister1(UserDto userDto) {
        log.info("用户{}注册成功,注册信息: {}", userDto.getName(), userDto.toString());
        // 发送邮件
        SendMailEvent sendMailEvent = new SendMailEvent(userDto);
        publisher.publishEvent(sendMailEvent);

		// 发送短息
        SendMsgEvent sendMsgEvent = new SendMsgEvent(userDto);
        publisher.publishEvent(sendMsgEvent);

        return String.format("用户%s注册成功,并发送邮件和短信通知", userDto.getName());
    }

}

2.4 事件异步处理(方式二有效)

2.4.1 配置线程池
package com.per.listener.e3.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Title ThreadPoolConfig
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-20
 */
@Configuration
public class ThreadPoolConfig {

    @Bean("eventListenerThreadPool")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setQueueCapacity(50);
        taskExecutor.setThreadNamePrefix("myExecutor--");
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(60);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }

}

2.4.2 设置事件执行的线程池
package com.per.listener.e3.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.Resource;

/**
 * @Title EventListenerAsynConfig
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-20
 */
@Configuration

public class EventListenerAsyncConfig {

    @Resource
    private ThreadPoolTaskExecutor eventListenerThreadPool;

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.setTaskExecutor(eventListenerThreadPool);
        return eventMulticaster;
    }
}

3 项目使用案例

场景:用户注册成功,给用户发送邮件、短信通知

3.1 创建事件父类

package com.per.listener.e3.event;

/**
 * @Title AbstractEvent 所有事件父类
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public abstract class AbstractEvent {

    /**
     * 事件源
     */
    protected Object source;

    public AbstractEvent(Object source) {
        this.source = source;
    }

    /**
     * 获取事件源
     *
     * @return
     */
    public Object getSource() {
        return source;
    }

    /**
     * 设置事件源
     *
     * @param source
     */
    public void setSource(Object source) {
        this.source = source;
    }
}

3.2 创建监听器接口

package com.per.listener.e3.listener;

import com.per.listener.e3.event.AbstractEvent;

/**
 * @Title EventListener 事件监听器接口
 * @ProjectName spring-boot-demo
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public interface EventListener<E extends AbstractEvent> {

    /**
     * 处理事件
     *
     * @param event 要处理的事件
     */
    void onEvent(E event);
}

3.3 创建事件监听器管理类

package com.per.listener.e3.listener;

import com.per.listener.e3.event.AbstractEvent;

/**
 * @Title EventListenerManager 事件监听器管理类
 * @ProjectName spring-boot-demo
 * @Description 1.负责事件监听器的管理(注册监听器&移除监听器,将事件和监听器关联起来)
 *              2.负责事件的广播(将事件广播给所有的监听器,对该事件感兴趣的监听器会处理该事件)
 * @Author Lee
 * @Date 2024-01-17
 */
public interface EventListenerManager {

    /**
     * 广播事件给所有监听器
     *
     * @param event 事件
     */
    void pushEvent(AbstractEvent event);

    /**
     * 添加一个事件监听器
     *
     * @param listener 事件监听器
     */
    void addListener(EventListener<?> listener);

    /**
     * 删除一个事件监听器
     *
     * @param listener 事件监听器
     */
    void removeListener(EventListener<?> listener);
}

3.4 创建事件监听器的简单实现

package com.per.listener.e3.listener.impl;

import com.per.listener.e3.event.AbstractEvent;
import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Title SimpleEventListener 事件广播器的简单实现
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public class SimpleEventListener implements EventListenerManager {

    private Map<Class<?>, List<EventListener>> eventListenerMap = new ConcurrentHashMap<>();

    @Override
    public void pushEvent(AbstractEvent event) {
        List<EventListener> eventListeners = this.eventListenerMap.get(event.getClass());
        if (eventListeners != null) {
            for (EventListener eventListener : eventListeners) {
                // 执行事件
                eventListener.onEvent(event);
            }
        }
    }

    @Override
    public void addListener(EventListener<?> listener) {
        Class<?> eventType = this.getEventType(listener);
        // 查询map中事件对应的监听器
        List<EventListener> eventListeners = this.eventListenerMap.get(eventType);
        if (eventListeners == null) {
            // 事件对应的监听器集合为空,保存事件和监听器到map中,key:事件 value:空的监听器集合
            eventListeners = new ArrayList<>();
            this.eventListenerMap.put(eventType, eventListeners);
        }
        // 事件对应的监听器集合不为空,添加监听器到事件对应的监听器集合中
        eventListeners.add(listener);
    }

    @Override
    public void removeListener(EventListener<?> listener) {
        Class<?> eventType = this.getEventType(listener);
        // 查询map中事件对应的监听器
        List<EventListener> eventListeners = this.eventListenerMap.get(eventType);
        if (eventListeners != null) {
            // 事件对应的监听器集合不为空,从事件对应的监听器集中删除当前监听
            eventListeners.remove(listener);
        }
    }

    protected Class<?> getEventType(EventListener eventListener) {
        // 获取直接实现eventListener接口的类或接口的Type
        // 异步@Async时使用这种方式获取
//        ParameterizedType parameterizedType = (ParameterizedType)eventListener.getClass().getSuperclass().getGenericInterfaces()[0];
        // 同步时使用这种方式获取
        ParameterizedType parameterizedType = (ParameterizedType) eventListener.getClass().getGenericInterfaces()[0];
        // 获取EventListener中泛型的实际类型
        Type eventType = parameterizedType.getActualTypeArguments()[0];
        return (Class<?>) eventType;
    }
}

3.5 创建用户注册事件

package com.per.listener.e3.event;

import com.per.domain.UserDto;

/**
 * @Title RegisterSuccessEvent 用户注册成功事件
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
public class RegisterSuccessEvent extends AbstractEvent {

    /**
     * 用户信息
     */
    private UserDto userDto;

    /**
     * 用户注册成功事件
     *
     * @param source  事件源
     * @param userDto 用户信息
     */
    public RegisterSuccessEvent(Object source, UserDto userDto) {
        super(source);
        this.userDto = userDto;
    }

    public UserDto getUserDto() {
        return userDto;
    }

    public void setUserDto(UserDto userDto) {
        this.userDto = userDto;
    }
}

3.6 创建用户注册服务

package com.per.service.impl;

import com.per.domain.UserDto;
import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListenerManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Title UserRegisterService 用户注册成功服务
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Slf4j
public class UserRegisterService {

    /**
     * 事件发布者
     */
    @Autowired
    private EventListenerManager eventListenerManager;

    /**
     * 用户注册
     *
     * @param userDto 用户信息
     */
    public void registerUser(UserDto userDto) {
        log.info("用户{}注册成功", userDto.getName());
        // 执行其他监听事件
        this.eventListenerManager.pushEvent(new RegisterSuccessEvent(this, userDto));
    }

    public EventListenerManager getEventListenerManager() {
        return eventListenerManager;
    }

    public void setEventListenerManager(EventListenerManager eventListenerManager) {
        this.eventListenerManager = eventListenerManager;
    }
}

3.7 创建事件监听配置类

package com.per.listener.e3.config;

import com.per.listener.e3.listener.EventListener;
import com.per.listener.e3.listener.EventListenerManager;
import com.per.listener.e3.listener.impl.SimpleEventListener;
import com.per.service.impl.UserRegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Title EventListenerConfig
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Configuration
@Component
public class EventListenerConfig {

    /**
     * 注册一个事件发布者
     *
     * @param eventListeners 事件
     * @return
     */
    @Bean
    @Autowired(required = false) // 当eventListeners不存在时不抛出异常
    public EventListenerManager eventListenerManager(List<EventListener> eventListeners) {
        EventListenerManager eventListenerManager = new SimpleEventListener();
        if (eventListeners != null) {
            eventListeners.forEach(eventListener -> eventListenerManager.addListener(eventListener));
        }
        return eventListenerManager;
    }

    /**
     * 注册一个用户注册服务
     *
     * @param eventListenerManager
     * @return
     */
    @Bean
    public UserRegisterService userRegisterService(EventListenerManager eventListenerManager) {
        UserRegisterService userRegisterService = new UserRegisterService();
        userRegisterService.setEventListenerManager(eventListenerManager);
        return userRegisterService;
    }
}

3.8 创建发送邮件服务

package com.per.service.impl;

import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Title UserSendMailService 用户发送邮件服务
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Component
@Slf4j
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {

    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("给用户{}发送邮件,内容:恭喜你注册成功", event.getUserDto().getName());
    }
}

3.9 创建发送短信服务

package com.per.service.impl;

import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Title UserSendMsgService 用户发送短信服务
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-17
 */
@Component
@Slf4j
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {
    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("给用户{}发送短息,短信内容:恭喜你注册成功", event.getUserDto().getName());
    }
}

3.10 调用用户注册服务

    /**
     * 用户注册成功后发送邮件、发送短信
     *
     * @return
     */
    @RequestMapping(value = "register", method = RequestMethod.GET)
    public String register() {
        UserDto userDto = new UserDto(11, "李四", "男");
        userRegisterService.registerUser(userDto);
        return "SUCCESS";
    }

3.11 调用赠送优惠券服务(扩展)

新增场景:增加一个赠送优惠券业务

package com.per.service.impl;

import com.per.listener.e3.event.RegisterSuccessEvent;
import com.per.listener.e3.listener.EventListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Title SendUserCouponsService 发送优惠券服务
 * @Description TODO
 * @Author Lee
 * @Date 2024-01-20
 */
@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {

    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("给用户{}发送100元优惠券", event.getUserDto().getName());
    }
}

4 事件异步处理

注意:下面的修改皆基于3 项目使用案例修改

4.1 启动类开启异步

package com.per;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync(proxyTargetClass = true)
public class SpringBootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }

}

4.2 创建线程池

参考 2.4.1 配置线程池

4.3 事件方法开启异步

@Component
@Slf4j
@Order(1)
public class UserSendMailService implements EventListener<RegisterSuccessEvent> {

    @Async("eventListenerThreadPool")
    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("{}-给用户{}发送邮件,内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());
    }
}

@Component
@Slf4j
@Order(2)
public class UserSendMsgService implements EventListener<RegisterSuccessEvent> {

    @Async("eventListenerThreadPool")
    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("{}--给用户{}发送短息,短信内容:恭喜你注册成功", Thread.currentThread().getName(), event.getUserDto().getName());
    }
}

@Component
@Slf4j
@Order(3)
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {

    @Async("eventListenerThreadPool")
    @Override
    public void onEvent(RegisterSuccessEvent event) {
        log.info("{}--给用户{}发送100元优惠券", Thread.currentThread().getName(), event.getUserDto().getName());
    }
}

4.4 修改事件类型的获取方式

参考 3.4 创建事件监听器的简单实现

5 拓展

5.1 控制发送邮件、短信、优惠券服务的执行顺序

可以通过@Order控制服务的加载顺序实现

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot实现监听器的过程是通过定义监听器类并注册到Spring容器中来实现的。首先,我们需要创建一个监听器类来监听特定的事件。在SpringBoot中,我们可以通过实现ApplicationListener接口来创建监听器类。然后,我们可以在监听器类中重写onApplicationEvent方法,该方法会在监听到指定事件发生时被调用。 接下来,我们需要将监听器注册到Spring容器中。在SpringBoot中,我们可以通过在监听器类上添加@Component注解来将其注册为一个Bean。这样,Spring容器在启动时会自动扫描并注册这个监听器。 在注册监听器后,当指定的事件发生时,Spring容器会根据监听器的优先级顺序依次调用监听器的onApplicationEvent方法。在这个方法中,我们可以编写我们需要执行的逻辑。 总结起来,SpringBoot实现监听器的步骤如下: 1. 创建一个监听器类,并实现ApplicationListener接口。 2. 在监听器类中重写onApplicationEvent方法,编写监听事件后的逻辑。 3. 在监听器类上添加@Component注解,将其注册为一个Bean。 4. 在SpringBoot启动时,Spring容器会自动扫描并注册这个监听器。 5. 当指定事件发生时,Spring容器会自动调用监听器的onApplicationEvent方法执行相应的逻辑。 引用参考资料: :《springboot中使用监听器》 :《SpringBoot实现拦截器、过滤器、监听器过程解析》 :《目前只有一个实现类EventPublishingRunListener,它把监听的过程过装成了SpringApplicationEvent事并通过内部属性广播出去,属性名为initialMulticaster,是ApplicationEventMulticaster接口的实现类 SimpleApplicstionEventMulticaster.广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值