Springboot之监听器

发布时间:2024年01月20日

事件监听的几种方式

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

}

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
        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
public class SendUserCouponsService implements EventListener<RegisterSuccessEvent> {

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

4 拓展

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

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

文章来源:https://blog.csdn.net/songdancer/article/details/135708316
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。