美文网首页嘟嘟程序猿
SpringBoot--开发实战--监听器(四十一)

SpringBoot--开发实战--监听器(四十一)

作者: 无剑_君 | 来源:发表于2019-08-09 14:16 被阅读14次

    一、ApplicationListener

    ApplicationContext提供事件处理通过ApplicationEvent类和ApplicationListener接口。如果一个bean实现ApplicationListener接口在容器中,每次一个ApplicationEvent被发布到ApplicationContext中,这类bean就会收到这些通知。
    实现Spring事件机制主要有4个类:
    ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
    ApplicationListener:事件监听器,用于接收事件处理时间。
    ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
    ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。

    1. 事件就是一个包含了任意对象并含有事件对象创建时间戳的类。
      框架源码:
    public abstract class ApplicationEvent extends EventObject {
        private static final long serialVersionUID = 7099057708183571937L;
        private final long timestamp = System.currentTimeMillis();
    
        public ApplicationEvent(Object source) {
            super(source);
        }
    
        public final long getTimestamp() {
            return this.timestamp;
        }
    }
    
    1. ApplicationListener
      框架源码:
    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
        void onApplicationEvent(E var1);
    }
    

    当事件监听器接收到它可以处理的事件,会调用onApplicationEvent()方法。注意到ApplicationListener是泛型参数的这样可以参数化的定制事件。这意味着onApplicationEvent()方法可以保持类型安全,避免任何需要向下类型转换。你可以尽可能多的注册你希望事件侦听器,但是注意,默认情况下,事件监听器同步接收事件。这意味着publishEvent()方法会阻塞直到所有的事件监听器成处理完事件。这种单线程同步方法的一个特点是,当一个监听器接收到一个事件时,它运行在事务上下文的发布者线程上如果事务上下文可用。如果事件的发布需要另一种策略(譬如多线程)需要实现自己的 ApplicationEventMulticaster接口类。

    1. ApplicationEventMulticaster
      ApplicationEventMulticaster接口方法分为三类,注册事件监听器、移除事件监听器、发布事件。

    二、Spring内置事件

    事件 描述
    ContextRefreshedEvent 事件发布在ApplicationContext初始化或刷新时(例如,通过在ConfigurableApplicationContext接口使用refresh()方法)。这里,“初始化”意味着所有bean加载,post-processor bean被检测到并且激活,单例预先实例化,ApplicationContext对象可以使用了。只要上下文没有关闭,可以触发多次刷新,ApplicationContext提供了一种可选择的支持这种“热”刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext并非如此。具体是在AbstractApplicationContext的finishRefresh()方法中。
    ContextStartedEvent 事件发布在ApplicationContext开始使用ConfigurableApplicationContext接口start()方法。这里,“开始”意味着所有生命周期bean接收到一个明确的起始信号。通常,这个信号用于明确停止后重新启动,但它也可以用于启动组件没有被配置为自动运行(例如,组件还没有开始初始化)。
    ContextStoppedEvent 事件发布在ApplicationContext停止时通过使用ConfigurableApplicationContext接口上的stop()方法。在这里,“停止”意味着所有生命周期bean接收一个显式的停止信号。停止上下文可以通过重新调用start()方法。
    ContextClosedEvent 事件发布在ApplicationContext关闭时通过关闭ConfigurableApplicationContext接口()方法。这里,“封闭”意味着所有单例bean被摧毁。一个封闭的环境达到生命的终结。它不能刷新或重启。
    RequestHandledEvent 一个特定的web事件告诉所有能处理HTTP请求的bean 。这个事件是在请求完成后发布的。这个事件只适用于使用Spring的DispatcherServlet的web应用程序。

    三、示例

    当一个用户完成贷款订单后,我们希望执行发送提醒短信、调用积分服务增加积分、通知风控服务重算风控值(后续操作可能增加)等功能。

    //创建订单
    public void createOrder(Order order){
    创建贷款订单;
    发送提醒短信;
    调用积分服务增加积分;
    调用风控服务推送订单信息;
    ……
    返回;
    }

    随着业务复杂度的增加,我们很快发现createOrder()创建订单这个方法耦合了太多与注册无关的逻辑,即影响了原本创建订单方法的效率,在设计上又不符合“开闭原则”。
    现在使用spring事件机制我们来解耦,将与注册无关的操作改为异步。这里直接使用注解式写法。

    1. 创建订单实体
    @Data
    @Component
    public class Order {
        private String orderNo;
        private String phone;
    }
    
    1. 创建事件
    /**
     * 创建订单完成事件
     */
    @Component
    public class AfterCreateOrderEvent extends ApplicationEvent {
        public AfterCreateOrderEvent(Order order) {
            super(order);
        }
        public Order getOrder(){
            return (Order) getSource();
        }
    }
    
    1. 创建业务类进行事件发布与监听
    @Service
    public class OrderService {
        //直接注入spring事件发布者
        @Autowired
        private ApplicationEventPublisher applicationEventPublisher;
    
        /**
         * 简单的创建订单方法
         */
        public void createOrder(Order order) {
            System.out.println("创建订单 order:" + order.getOrderNo() + " 结束");
            // TODO 1. 调用事件发布者发布事件,解耦
            applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));
            System.out.println("createOrder方法 结束");
        }
    
        // TODO 2. 事件监听处理,加入@EventListener注解后,该方法可以看出一个事件监听者
        @EventListener
        public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
            Order order = afterCreateOrderEvent.getOrder();
            Thread.sleep(2000);
            System.out.println("调用短信通知服务:" + order.getPhone());
            System.out.println("调用积分服务增加贷款积分:" + order.getOrderNo());
        }
    }
    
    1. 测试
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringSessionApplicationTests {
        @Autowired
        private OrderService orderService;
    
        @Test
        public void eventTest() {
            Order order = new Order();
            order.setOrderNo("N123124124124");
            order.setPhone("18782202534");
            orderService.createOrder(order);
        }
    }
    
    结果

    相关文章

      网友评论

        本文标题:SpringBoot--开发实战--监听器(四十一)

        本文链接:https://www.haomeiwen.com/subject/injxjctx.html