Spring 的事件发布机制

Spring 的事件发布机制

0 概述

  • 在 Spring 框架中,事件发布机制是一种非常重要的通信方式,它允许不同的组件之间进行松耦合的交互。通过事件发布机制,一个组件可以发布事件,而其他组件可以监听这些事件并在事件发生时执行相应的操作。

1 使用场景

  • 状态通知:当一个对象的状态发生变化时,可以发布事件来通知其他对象。例如,订单状态从“待支付”变为“已支付”时,可以发布一个事件,通知相关系统进行后续处理。
  • 业务解耦:通过事件,可以将业务逻辑分散到不同的组件中,降低系统的耦合度。例如,用户注册成功后,可以发布一个用户注册事件,由邮件服务监听并发送欢迎邮件,由积分服务监听并赠送积分。
  • 异步处理:事件监听器可以异步地处理事件,提高系统的响应性能。例如,当用户上传头像后,可以发布一个头像上传事件,由后台服务异步处理图片的压缩和存储。

2 优缺点

  • 优点
    • 松耦合:事件的发布者和监听者不需要直接依赖对方,降低了系统的耦合度。
    • 灵活:事件的监听和处理逻辑可以动态地添加或移除,提高了系统的可扩展性。
    • 异步:事件的处理可以是异步的,提高了系统的性能。
  • 缺点
    • 性能开销:事件机制相比直接调用会引入额外的性能开销,特别是在高并发场景下。
    • 复杂性:随着系统中事件和监听器的增多,事件的管理和调试可能会变得更加复杂。
    • 一致性问题:如果事件的处理是异步的,需要特别注意保证数据的一致性和事务的完整性。

3 Demo 示例

  • 需求:

    用户登录成功之后,需要做以下操作:
        1、【系统服务】需要记录用户登录时间
        2、【账户服务】需要自动给用户增加积分
        3、【营销服务】需要给用户推送优惠券

  • 下面我将针对用户登录场景的需求进行 Spring 事件发布和监听的示例演示:

  • 登录实体对象:

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class User {
    
        private Long id;
    
        private String name;
    
        private String password;
    
        private String sex;
    
        private Integer age;
    
        public User(String name, String password) {
            this.name = name;
            this.password = password;
        }
        
    }
    
  • 定义事件:

    import org.springframework.context.ApplicationEvent;
    import top.ezjava.java17demo.pojo.User;
    
    /**
     * 登录事件
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
    public class LoginEvent extends ApplicationEvent {
    
        /**
         * @param source 代表是谁登录成功
         */
        public LoginEvent(User source) {
            super(source);
        }
    }
    
  • 创建【系统服务】事件监听器:

    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Service;
    import top.ezjava.java17demo.event.LoginEvent;
    import top.ezjava.java17demo.pojo.User;
    /**
     * 系统服务
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
    @Service
    public class SysServiceImpl {
    
        @EventListener
        public void onEvent(LoginEvent event) {
            System.out.println("-----系统服务收到了事件-----");
            User source = (User) event.getSource();
            recordLogin(source.getName());
        }
    
        public void recordLogin(String username) {
            System.out.println(username + " 登录成功,时间:" + System.currentTimeMillis());
        }
        
    }
    
  • 创建【账户服务】事件监听器:

    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Service;
    import top.ezjava.java17demo.event.LoginEvent;
    import top.ezjava.java17demo.pojo.User;
    
    /**
     * 账户服务
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
     public class AccountServiceImpl implements ApplicationListener<LoginEvent> {
    	@override
    	public void onApplicationEvent(LoginEvent event) {
    		System.out.println("-----账户服务收到了事件-----");
    		User source = (User) event.getSource();
    		addAccountScore(source.getName());
    	}
    
    	public void addAccountScore(String username) {
    		System.out.println(username + " 增加了 10 积分");
    	}
    	
     }
    
  • 创建【营销服务】事件监听器:

    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Service;
    import top.ezjava.java17demo.event.LoginEvent;
    import top.ezjava.java17demo.pojo.User;
    
    /**
     * 营销服务
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
     public class CouponServiceImpl {
     
    	@EventListener
    	public void onEvent(LoginEvent event) {
    		System.out.println("-----营销服务收到了事件-----");
    		User source = (User) event.getSource();
    		sendCoupon(source.getName());
    	}
    
    	public void sendCoupon(String username) {
    		System.out.println(username + " 得到一张 10 元优惠券");
    	}
    	
     }
    
  • 事件发布器:

    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Component;
    
    /**
     * 自定义事件发送器
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
    @Component
    public class EventPublisher implements ApplicationEventPublisherAware {
    
        /**
         * 实现ApplicationEventPublisherAware接口,所有监听这个事件的监听器都可以收到
         */
        private ApplicationEventPublisher applicationEventPublisher;
    
        /**
         * 这个方法是自己写的发送事件的方法,最终会调用 Spring 的事件发送器
         * @param event 事件
         */
        public void sendEvent(ApplicationEvent event) {
            // 最终调用 Spring 的事件发送器
            applicationEventPublisher.publishEvent(event);
        }
    
        /**
         * setter 注入
         * @param applicationEventPublisher event publisher to be used by this object
         */
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
        
    }
    
  • Controller 层:

    import jakarta.annotation.Resource;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import top.ezjava.java17demo.event.EventPublisher;
    import top.ezjava.java17demo.event.LoginEvent;
    import top.ezjava.java17demo.pojo.User;
    
    /**
     * @Author Jasper
     * @Time 2024/02/04
     * @公众号:EzCoding
     */
    @RestController
    public class LoginController {
    
        @Resource
        private EventPublisher eventPublisher;
    
        @GetMapping("/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password) {
    
            // 登录业务处理
            System.out.println("登录业务处理完成...");
            // 创建事件信息,并发送
            LoginEvent loginEvent = new LoginEvent(new User("Jasper", "EzCoding"));
            eventPublisher.sendEvent(loginEvent);
            return username + "登陆成功";
        }
    }
    
  • 访问测试:localhost:8080/login?username=Jasper&password=Ezcoding

  • 控制台输出结果如下:

    登录业务处理完成...
    -----账户服务收到了事件-----
    Jasper 增加了 10 积分
    -----营销服务收到了事件-----
    Jasper 得到一张 10 元优惠券
    -----系统服务收到了事件-----
    Jasper 登录成功,时间:1706975462642
    
  • 这样就完成了上述需求,用户登录成功后,对其余三个事件进行解耦执行

  • 以上就是 Spring 事件发布机制的全部内容了,其他小伙伴还有补充的话,可以在评论区讨论~

  • 创作不易,感谢阅读,若遇到问题,可以关注此微信gzh:EzCoding 留言反馈,希望能够帮助到您

你可能感兴趣的:(spring,java,spring,boot,后端)