• spring事件机制


    将一个流程做成低耦合可扩展性能:

    1.注册一个用户person

    2.注册之后给用户发送短信注册成功

    3.注册之后给用户发送邮件最近活动信息

    事件机制:

    事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点

    Spring 中事件机制中各角色:

    • 事件 ApplicationEvent 是所有事件对象的父类,也就是说当某个业务发生改变 Spring 可以发出一个事件出来(当然这边可能是具体的某一个事件,Spring 中常用的事件请看第二节介绍)。
    • 事件监听 ApplicationListener,也就是观察者,继承自 JDK 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。
    • 事件源 ApplicationContext,ApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。
    • 事件管理 ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

    事件:注册一个用户

    监听器:发送短信、发送邮件

    增加了一个Listener来解耦UserService和其他服务,即注册成功后,只需要通知相关的监听器,不需要关系它们如何处理。增删功能非常容易。这就是一个典型的事件处理模型/观察者,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。

    具体示例:

    1.定义事件

    public class UserRegisterEvent extends ApplicationEvent {
    
        private String userName;
        //source事件源
        public UserRegisterEvent(Object source, String userName) {
            super(source);
            this.userName = userName;
        }
        public String getUserName() {
            return userName;
        }
    
    }

    2.定义监听器(两种方式)

    方式1)实现接口方式:

    /**
     * 事件监听器:监听广播,发送邮件
     */
    @Component
    public class UserRegisterEventListener implements ApplicationListener<UserRegisterEvent> {
        @Override
        public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
            System.out.println(String.format("接口模式给用户[%S]发送邮件成功",userRegisterEvent.getUserName()));
        }
    }

    方式2)注解方式:

    /**
     * 事件监听器:监听广播,发送邮件
     */
    @Component
    public class UserRegisterEventListener2 {
    
        @EventListener(classes = {UserRegisterEvent.class})
        public void sendMail(UserRegisterEvent userRegisterEvent){
            System.out.println("注解1给用户"+userRegisterEvent.getUserName()+"发送邮件成功");
        }
        @EventListener(UserRegisterEvent.class)
        public void sendCompon(UserRegisterEvent userRegisterEvent){
            System.out.println(String.format("注解2给用户[%s]发送邮件成功",userRegisterEvent.getUserName()));
        }
    
    }

    第三、第四种摘取往上,

    第三种方式:有序监听器实现SmartApplicationListener接口:

    package com.br.listener;
    
    import com.br.bean.User;
    import com.br.event.UserRegisterEvent;
    import com.br.service.UserService;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.SmartApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @author 10400
     * @create 2018-02-27 15:03
     * @deprecated 有序监听
     */
    @Component
    public class SmartRegisterListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
            //只有UserRegisterEvent监听类型才会执行下面逻辑
            return aClass == UserRegisterEvent.class;
        }
    
        @Override
        public boolean supportsSourceType(Class<?> aClass) {
            //只有在UserService内发布的UserRegisterEvent事件时才会执行下面逻辑
            return aClass == UserService.class;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
            //转换事件类型
            UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
            //获取注册用户对象信息
            User user = userRegisterEvent.getUser();
            //.../完成注册业务逻辑
    
            System.out.println("SmartRegisterListener" + user.getName());
    
        }
    
        /**
         * return 的数值越小证明优先级越高,执行顺序越靠前。
         * @return
         */
        @Override
        public int getOrder() {
            return 10;
        }
    }
    SmartApplicationListener接口继承了全局监听ApplicationListener,
    并且泛型对象使用的ApplicationEvent来作为全局监听,
    可以理解为使用SmartApplicationListener作为监听父接口的实现,监听所有事件发布。

    getOrder:return的数值越小证明优先级越高,执行顺序越靠前

    第四种方式:使用@Async实现异步监听

    @Aysnc其实是Spring内的一个组件,可以完成对类内单个或者多个方法实现异步调用,这样可以大大的节省等待耗时。内部实现机制是线程池任务ThreadPoolTaskExecutor,通过线程池来对配置@Async的方法或者类做出执行动作。

    • 线程任务池配置
      我们创建一个ListenerAsyncConfiguration,并且使用@EnableAsync注解开启支持异步处理,具体代码如下所示:
    @Configuration
    @EnableAsync
    public class ListenerAsyncConfiguration implements AsyncConfigurer
    {
        /**
         * 获取异步线程池执行对象
         * @return
         */
        @Override
        public Executor getAsyncExecutor() {
            //使用Spring内置线程池任务对象
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            //设置线程池参数
            taskExecutor.setCorePoolSize(5);
            taskExecutor.setMaxPoolSize(10);
            taskExecutor.setQueueCapacity(25);
            taskExecutor.initialize();
            return taskExecutor;
        }
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return null;
        }
    }
    我们自定义的监听异步配置类实现了AsyncConfigurer接口并且实现内getAsyncExecutor方法以提供线程任务池对象的获取。
    我们只需要在异步方法上添加@Async注解就可以实现方法的异步调用,为了证明这一点,我们在发送邮件onApplicationEvent方法内添加线程阻塞3秒,修改后的代码如下所示:

    /**
         * supportsEventType & supportsSourceType 两个方法返回true时调用该方法执行业务逻辑
         * @param applicationEvent 具体监听实例,这里是UserRegisterEvent
         */
        @Override
        @Async
        public void onApplicationEvent(ApplicationEvent applicationEvent) {
            try {
                Thread.sleep(3000);//静静的沉睡3秒钟
            }catch (Exception e)
            {
                e.printStackTrace();
            }
            //转换事件类型
            UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent;
            //获取注册用户对象信息
            UserBean user = userRegisterEvent.getUser();
            System.out.println("用户:"+user.getName()+",注册成功,发送邮件通知。");
        }


    网摘链接:https://www.jianshu.com/p/ef2cee8c5dd1

    3.事件服务(任取一个事件管理实现接口即可ApplicationContextAware、ApplicationEventPublisherAware)

    @Service
    public class UserRegisterEventService implements ApplicationContextAware, ApplicationEventPublisherAware {
    
        private static ApplicationContext applicationContext;
        private static ApplicationEventPublisher applicationEventPublisher;
    
        //注册
        public void registerUser(String userName) {
            UserRegisterEvent userRegisterEvent = new UserRegisterEvent(UserRegisterEvent.class, userName);
            System.out.println(String.format("用户[%s]注册成功", userName));
            //发布注册成功事件
            applicationContext.publishEvent(userRegisterEvent);
            applicationEventPublisher.publishEvent(userRegisterEvent);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        @EventListener(classes = {UserRegisterEvent.class})
        public void sendMail(UserRegisterEvent userRegisterEvent){
            System.out.println("注解3给用户"+userRegisterEvent.getUserName()+"发送邮件成功");
        }
        @EventListener(UserRegisterEvent.class)
        public void sendCompon(UserRegisterEvent userRegisterEvent){
            System.out.println(String.format("注解4给用户[%s]发送邮件成功",userRegisterEvent.getUserName()));
        }
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    }

    测试用例(结合上一篇测试用例规范注入Service服务):

    /**
     * 事件对象:用户注册事件
     */
    @RunWith(SpringRunner.class)
    //@SpringBootTest(classes = UserRegisterEventConfig.class)
    @ContextConfiguration(classes = UserRegisterEventConfig.class)
    public class UserRegisterTest {
    
        @Resource
        private UserRegisterEventService userRegisterEventService;
    
        @Test
        public void test() {
    //        ApplicationContext context = new AnnotationConfigApplicationContext(UserRegisterEventService.class);
    //        UserRegisterEventService userRegisterEventService = context.getBean(UserRegisterEventService.class);
            System.out.println(userRegisterEventService);
            userRegisterEventService.registerUser("userName222");
        }
    
    }
  • 相关阅读:
    用document.onreadystatechange和document.readyState确保文档加载完毕才获取DOM
    动态修改样式和层叠样式表
    jQuery中Ajax事件顺序及各参数含义
    对于JavaScript对象的prototype和__proto__的理解
    HTML5实现“摇一摇”效果
    修改mysql错误提示语言的方法
    12个非常有用的JavaScript小技巧
    学习javascript中this用法的一些感悟
    Token 认证
    “好”的接口是怎么样的?
  • 原文地址:https://www.cnblogs.com/mangwusuozhi/p/15476680.html
Copyright © 2020-2023  润新知