zoukankan      html  css  js  c++  java
  • spring event

    昨天看到了一遍关于spring event的帖子,觉得很好,就照着敲了一份代码,感觉对spring event有了进一步的认识。帖子链接:https://segmentfault.com/a/1190000011433514。

    spring event 封装了底层,为我们方便提供了事件的发布、消息订阅。其底层原理是观察者模式。

    下面直接上代码:

    定义一个bean如下,使用了lombok插件(真的挺好用):

    package com.abc.model;
    
    import lombok.Data;
    
    @Data
    public class User {
        private String name;
        private String password;
    }

    定义一个一个注册事件:

    package com.abc.event;
    
    import com.abc.model.Dog;
    import com.abc.model.User;
    import lombok.Getter;
    import org.springframework.context.ApplicationEvent;
    
    @Getter
    public class UserRegisterEvent extends ApplicationEvent {
    
        private User user;
    
        public UserRegisterEvent(Object source, User user) {
            super(source);
            this.user = user;
        }
    }

    编写service:

    这里说明一下,applicationContext.publishEvent(new UserRegisterEvent(this, user)) 这个是发布事件的方法。到这里就完成了事件的发布了。当controller里面请求时候,service响应发布事件。

    package com.tuandai.service;
    
    import com.tuandai.event.UserRegisterEvent;
    import com.tuandai.model.Dog;
    import com.tuandai.model.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserRegisterService {
        @Autowired
        private ApplicationContext applicationContext;
    
        //发布事件
        public void register(User user) {
            //做些其他业务
            applicationContext.publishEvent(new UserRegisterEvent(this, user));
        }
    }
    View Code

    定义监听器,有两种方式。

    第一种,使用@EventListener 注解方式:

    package com.tuandai.listener;
    
    import com.tuandai.event.UserRegisterEvent;
    import com.tuandai.model.User;
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class AnnotationListener {
        @EventListener 
        //这里如果加上@EventListener,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
        public void register(UserRegisterEvent userRegisterEvent) {
            User user = userRegisterEvent.getUser();
            System.out.println("@UserRegisterEvent注册信息,狗命:" + user.getName() + ",密码:" + user.getPassword());
        }
    }
    View Code

    第二种,继承@ApplicationListener接口的方式,重写onApplicationEvent方法:

    package com.tuandai.listener;
    
    import com.tuandai.event.UserRegisterEvent;
    import com.tuandai.model.User;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    //@Component 这里如果加上@Component,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
    public class RegisterListener implements ApplicationListener<UserRegisterEvent> {
        @Override
        public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
            //做些其他业务
            User user=userRegisterEvent.getUser();
    
            System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());
        }
    }
    package com.abc.listener;

    import com.abc.event.UserRegisterEvent;
    import com.abc.model.User;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;

    @Component
    //这里如果加上@Component,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
    public class RegisterListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
    //做些其他业务
    User user=userRegisterEvent.getUser();

    System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());
    }
    }

    请求controller:http://localhost:9001/register?name=admin&password=123

    结果如下:

    可以看到,的确是做到了观察者的模式,订阅了消息。

    下面再定义一个监听器,用于在注册之后发送邮件,这个也是基于@EventListener注解的方式的,代码如下:

    package com.tuandai.listener;
    
    import com.tuandai.event.UserRegisterEvent;
    import com.tuandai.model.User;
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class EmalRegisterListener {
        @EventListener 
        // 这里如果加上@EventListener,代码运行的时候就会自动扫描到这里,也就是会执行监听的动作
        public void sendEmal(UserRegisterEvent userRegisterEvent) {
            User user = userRegisterEvent.getUser();
            System.out.println("发送邮件,用户名是:" + user.getName() + ",密码是:" + user.getPassword());
        }
    }
    View Code

    再次运行,结果:

    在这里可以看到,是发送邮件的监听器先于注册监听器监听到注册信息,这个在实际的生产环境中,应该是先注册这块业务,然后再是发送邮件。为此,spring event提供了一个监听先后顺序的机制:SmartApplicationListener,可保证实际的监听效果符合我们的期望。

    定义一个有序的监听注册的监听器:

    这段代码中,重写的 supportsEventType方法,用于确保监听的事件是用户注册的事件,实际生产环境中,可以换成自己的定义的事件。

    package com.abc.listener;
    
    import com.abc.event.UserRegisterEvent;
    import com.abc.model.User;
    import com.abc.service.UserRegisterService;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.SmartApplicationListener;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SmartRegisterListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
            return aClass== UserRegisterEvent.class;
        }
    
        @Override
        public boolean supportsSourceType(Class<?> aClass) {
            return aClass== UserRegisterService.class;
        }
    
        /**
         * 只有supportsEventType和supportsSourceType返回true的时候,才执行这个方法
         * @param applicationEvent
         */
        @Override
      
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
            UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
            User user=userRegisterEvent.getUser();
            System.out.println("注册用户,用户名是:"+ user.getName());
        }
    
        @Override
        public int getOrder() {
            return 0;   //这里数字越小,监听的优先级越高
        }
    }

    定义一个智能的发送邮件的监听器:

    package com.tuandai.listener;
    
    import com.tuandai.event.UserRegisterEvent;
    import com.tuandai.model.User;
    import com.tuandai.service.UserRegisterService;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.SmartApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * 智能发送邮件监听器
     */
    @Component
    public class SmartEmailRegisterListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
            return aClass== UserRegisterEvent.class;
        }
    
        @Override
        public boolean supportsSourceType(Class<?> aClass) {
            return aClass== UserRegisterService.class;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
            UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
            User user=userRegisterEvent.getUser();
            System.out.println("发送邮件,用户名是:"+ user.getName());
        }
    
        @Override
        public int getOrder() {
            return 1;  //可以是负数
        }
    }
    View Code

    在这两段代码中,均有个getOrder()方法,用于定义该监听器的优先级。这个数字越小,优先级越高。

     再次执行,执行结果如下:

    可以看到已经是按照优先级,先注册用户,再发邮件了。

    以上的监听事件,都是同步的方式。实际生产环境你中,由于某些事件可能需要耗时较大,所有事件都用同步的方式,会降低体验效果。为此,spring也提供了异步的监听事件方式。

    异步监听的关键字:@Async 。但是单单使用这个关键字,还不能起作用,还需要有个自定义的配置,用于继承AsyncConfigurer。

    代码如下:

    package com.tuandai.confiuration;
    
    import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.AsyncConfigurer;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.stereotype.Component;
    
    import java.util.concurrent.Executor;
    
    @Configuration
    @EnableAsync   //需要加上这个注解。同事需要继承AsyncConfigurer这个才能实现异步
    public class ListenerAsyncConfiguration implements AsyncConfigurer {
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setCorePoolSize(3);
            threadPoolTaskExecutor.setMaxPoolSize(10);
            threadPoolTaskExecutor.setQueueCapacity(25);
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return null;
        }
    }
    View Code

    修改注册监听器:

    package com.abc.listener;

    import com.abc.event.UserRegisterEvent;
    import com.abc.model.User;
    import com.abc.service.UserRegisterService;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.SmartApplicationListener;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;

    @Component
    public class SmartRegisterListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
    return aClass== UserRegisterEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
    return aClass== UserRegisterService.class;
    }

    /**
    * 只有supportsEventType和supportsSourceType返回true的时候,才执行这个方法
    * @param applicationEvent
    */
    @Override
    @Async
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
    try {
    Thread.sleep(1000*10);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    UserRegisterEvent userRegisterEvent=(UserRegisterEvent) applicationEvent;
    User user=userRegisterEvent.getUser();
    System.out.println("注册用户,用户名是:"+ user.getName());
    }

    @Override
    public int getOrder() {
    return 0; //这里数字越小,监听的优先级越高
    }
    }

    在这段代码中,onApplicationEvent方法添加了@Async注解,声明这个方法是异步的方法。这段代码里,会沉睡10秒的时间。

    再次运行的时候,运行的速度很快,根本不用1s,证明这个监听器的异步的方式确是起作用了。

    执行结果如下:

    这里要注意到的是,发送邮件的监听器的执行,先于注册监听器了。之所以出现这种情况,是因为监听器在使用异步的方式的时候,getOrder()这个方法不再起作用了。

    因此,个人认为,实际生产环境中,异步的监听器应该是做一些不太重要,优先级不高,不需要实时的业务,比如主站注册,然后把数据同步到其他的副站。

    而同步的方式,则是用于做一些实时性较强,并且对顺序有着严格要求的业务。比如,先在主站注册,后发发送邮件。

  • 相关阅读:
    Cookie基本使用
    Chartlet简单易用的图表控件
    JQuery 基础:6.Each的用法
    图的基本算法
    Head First Design Patterns Strategy Pattern
    个人整理的面试题
    Android从SIM卡中获取联系人
    Android 覆盖安装
    Head First Design Patterns Adapter Pattern
    android 获取sim卡运营商信息(转)
  • 原文地址:https://www.cnblogs.com/drafire/p/9271730.html
Copyright © 2011-2022 走看看