zoukankan      html  css  js  c++  java
  • 事件监听器模式与Spring事件机制

    1.事件监听器模式简单使用

      比如监听门开关改变事件以及name改变事件。

    1.事件相关类

    抽象门事件

    package cn.qlq.event.base;
    
    import java.util.EventObject;
    
    public abstract class DoorEvent extends EventObject {
    
        private static final long serialVersionUID = 7099057708183571937L;
    
        /**
         * 事件发生时间
         */
        private final long timestamp;
    
        public DoorEvent(Object source) {
            super(source);
            this.timestamp = System.currentTimeMillis();
        }
    
        public final long getTimestamp() {
            return this.timestamp;
        }
    }

    门name改变事件

    package cn.qlq.event.base;
    
    /**
     * 门改变事件
     * 
     * @author Administrator
     *
     */
    public class DoorNameChangeEvent extends DoorEvent {
    
        private static final long serialVersionUID = 2106840053610489753L;
    
        private String originName;
    
        private String newName;
    
        public DoorNameChangeEvent(Object source, String originName, String newName) {
            super(source);
            this.originName = originName;
            this.newName = newName;
        }
    
        public String getOriginName() {
            return originName;
        }
    
        public void setOriginName(String originName) {
            this.originName = originName;
        }
    
        public String getNewName() {
            return newName;
        }
    
        public void setNewName(String newName) {
            this.newName = newName;
        }
    
    }

    门状态改变事件

    package cn.qlq.event.base;
    
    /**
     * 门改变事件
     * 
     * @author Administrator
     *
     */
    public class DoorStatusChangeEvent extends DoorEvent {
    
        private static final long serialVersionUID = 2106840053610489753L;
    
        private String originStatus;
    
        private String newStatus;
    
        public DoorStatusChangeEvent(Object source, String originStatus, String newStatus) {
            super(source);
            this.originStatus = originStatus;
            this.newStatus = newStatus;
        }
    
        public String getOriginStatus() {
            return originStatus;
        }
    
        public void setOriginStatus(String originStatus) {
            this.originStatus = originStatus;
        }
    
        public String getNewStatus() {
            return newStatus;
        }
    
        public void setNewStatus(String newStatus) {
            this.newStatus = newStatus;
        }
    
    }

    2.监听器相关类

    抽象监听器类

    package cn.qlq.event.base;
    
    import java.util.EventListener;
    
    public interface DoorListener<E extends DoorEvent> extends EventListener {
    
        void onEvent(E event);
    
    }

    门name改变事件监听器

    package cn.qlq.event.base;
    
    public class DoorNameChangeListener implements DoorListener<DoorNameChangeEvent> {
    
        @Override
        public void onEvent(DoorNameChangeEvent event) {
            System.out.println("=============门name改变===========" + event.getTimestamp());
            System.out.println("原来name: " + event.getOriginName());
            System.out.println("最新name: " + event.getNewName());
        }
    }

    门status改变事件监听器

    package cn.qlq.event.base;
    
    public class DoorStatusChangeListener implements DoorListener<DoorStatusChangeEvent> {
    
        @Override
        public void onEvent(DoorStatusChangeEvent event) {
            System.out.println("=============门status改变===========" + event.getTimestamp());
            System.out.println("原来状态: " + event.getOriginStatus());
            System.out.println("最新状态: " + event.getNewStatus());
        }
    }

    3.门类

      addListener方法获取到泛型的实际参数然后将listener缓存到cache属性中,用于发布事件。publishEvent发布事件根据事件对应的typeName到cache中找到listener集合,然后发布事件。

    package cn.qlq.event.base;
    
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class Door {
    
        // 缓存事件监听器
        private Map<String, List<DoorListener<? extends DoorEvent>>> cache = new HashMap<>();
    
        public String status;
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            if (status != null && !status.equals(this.status)) {
                publishEvent(new DoorStatusChangeEvent(this, this.status, status));
            }
    
            this.status = status;
        }
    
        public void addListener(DoorListener<? extends DoorEvent> listener) {
            // 获取到接口。根据接口获取到泛型
            Type[] genericInterfaces = listener.getClass().getGenericInterfaces();
            Type type = genericInterfaces[0];
            if (type instanceof ParameterizedType) {
                ParameterizedType typeTmp = (ParameterizedType) type;
                // 原始类型。例如 interface cn.qlq.event.base.DoorListener
                Type rawType = typeTmp.getRawType();
                // 获取到实际参数类型
                Type[] actualTypeArguments = typeTmp.getActualTypeArguments();
                Type type2 = actualTypeArguments[0];
                String actuallyTypeName = type2.getTypeName();
                List<DoorListener<? extends DoorEvent>> list = cache.get(actuallyTypeName);
                if (list == null) {
                    list = new ArrayList<DoorListener<? extends DoorEvent>>();
                    cache.put(actuallyTypeName, list);
                }
                list.add(listener);
            }
        }
    
        public <E extends DoorEvent> void publishEvent(E event) {
            String typeName = event.getClass().getTypeName();
            List<DoorListener<? extends DoorEvent>> list = cache.get(typeName);
            for (DoorListener doorListener : list) {
                doorListener.onEvent(event);
            }
        }
    
    }

    4.客户端测试代码

    package cn.qlq.event.base;
    
    public class Client {
    
        public static void main(String[] args) {
    
            Door door = new Door();
            door.addListener(new DoorStatusChangeListener());
            door.addListener(new DoorNameChangeListener());
    
            // 发布事件
            door.publishEvent(new DoorNameChangeEvent(door, "木门", "铁门"));
    
            door.setStatus("close");
            door.setStatus("close");
            door.setStatus("open");
    
        }
    }

    结果:

    =============门name改变===========1594192130541
    原来name: 木门
    最新name: 铁门
    =============门status改变===========1594192130541
    原来状态: null
    最新状态: close
    =============门status改变===========1594192130541
    原来状态: close
    最新状态: open

    补充:Type与Class的区别

    查看JDK源码,发现有个Type类,而且是从1.5引入的。如下:

    /** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */
    package java.lang.reflect;
    
    /**
     * Type is the common superinterface for all types in the Java
     * programming language. These include raw types, parameterized types,
     * array types, type variables and primitive types.
     *
     * @since 1.5
     */
    public interface Type {
        /**
         * Returns a string describing this type, including information
         * about any type parameters.
         *
         * @implSpec The default implementation calls {@code toString}.
         *
         * @return a string describing this type
         * @since 1.8
         */
        default String getTypeName() {
            return toString();
        }
    }

    Type是Class的父接口。Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。Type可以表示出泛型的类型,而Class不能。

    如果想要获取泛型类型的数组,可以将Type转化为它的子接口ParameterizedType,通过getActualTypeArguments() 获取。参考上面的:Door类的addListener方法。

    另外:java中8种基本数据类型和void也有class对象。如下Integer类的方法:

        public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

    测试如下:

      getGenericSuperclass() 方法获取父类的Type;getGenericInterfaces()方法获取所有实现接口的Type。Type可以用于获取泛型的实际类型。ParameterizedType 类型的实例可以获取到原始类型和实际类型。

    package cn.qlq.event.base;
    
    import java.lang.reflect.Type;
    import java.lang.reflect.TypeVariable;
    import java.util.Arrays;
    
    public class Client<T> {
    
        private T prop;
    
        public static void main(String[] args) {
            System.out.println(int.class.isPrimitive());
            System.out.println(Integer.class.isPrimitive());
            System.out.println("===========");
    
            Client<String> client = new Client<String>();
            printObj(client);
    
            Integer integer = new Integer(1);
            printObj(integer);
    
            Integer[] integers = { 1, 3 };
            printObj(integers);
        }
    
        private static void printObj(Object obj) {
            System.out.println("===========");
            Class<?> class2 = obj.getClass();
            System.out.println(class2);
            System.out.println(class2.getTypeName());
            Type genericSuperclass2 = class2.getGenericSuperclass();
            System.out.println(genericSuperclass2);
            Type[] genericInterfaces2 = class2.getGenericInterfaces();
            System.out.println(Arrays.asList(genericInterfaces2));
            TypeVariable<?>[] typeParameters = class2.getTypeParameters();
            System.out.println(Arrays.asList(typeParameters));
        }
    
        public T getProp() {
            return prop;
        }
    
        public void setProp(T prop) {
            this.prop = prop;
        }
    
    }

    结果:

    true
    false
    ===========
    ===========
    class cn.qlq.event.base.Client
    cn.qlq.event.base.Client
    class java.lang.Object
    []
    [T]
    ===========
    class java.lang.Integer
    java.lang.Integer
    class java.lang.Number
    [java.lang.Comparable<java.lang.Integer>]
    []
    ===========
    class [Ljava.lang.Integer;
    java.lang.Integer[]
    class java.lang.Object
    [interface java.lang.Cloneable, interface java.io.Serializable]
    []

    2.Spring的事件

    1. 标准事件

      Spring的ApplicationContext 提供了支持事件和代码中监听器的功能。
      我们可以创建bean用来监听在ApplicationContext 中发布的事件。ApplicationEvent类和在ApplicationContext接口中处理的事件,如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

    Spring 提供了以下5种标准的事件:
    1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
    2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
    3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
    4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
    5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件

    比如经常在容器启动后创建默认用户等操作:

    package cn.qlq.event;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    import cn.qlq.service.user.UserService;
    
    /**
     * 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。
     * 也可以在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
     * 
     * @author Administrator
     *
     */
    @Component
    public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
        
        @Autowired
        private UserService userService;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            System.out.println("ContextRefreshedEvent=========== 容器启动完成");
            System.out.println(event);
            
            // 创建默认用户
    //        userService.addUser(user);
        }
    
    }

    启动后控制台:

    ContextRefreshedEvent=========== 容器启动完成
    org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@13b3d178, started on Wed Jul 08 16:28:45 CST 2020]
    2020/07/08-16:29:00 [main] INFO cn.qlq.MySpringBootApplication Started MySpringBootApplication in 15.976 seconds (JVM running for 17.039)

    2.自定义事件

    1.定义事件

    package cn.qlq.event;
    
    import org.springframework.context.ApplicationEvent;
    
    public class CustomApplicationEvent extends ApplicationEvent {
    
        private String msg;
    
        private static final long serialVersionUID = -9184671635725233773L;
    
        public CustomApplicationEvent(Object source, final String msg) {
            super(source);
            this.msg = msg;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
    }

    2.定义监听器

    package cn.qlq.event;
    
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomEventListener implements ApplicationListener<CustomApplicationEvent> {
        @Override
        public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
            // handle event
            System.out.println("收到事件,消息为:" + applicationEvent.getMsg());
            System.out.println(applicationEvent);
        }
    }

    3.代码发布事件

    @Controller
    public class LoginController {
    
        @Autowired
        private ApplicationContext applicationContext;/**
         * 跳转到登陆界面
         * 
         * @return
         */
        @RequestMapping("login")
        public String login() {
            applicationContext.publishEvent(new CustomApplicationEvent(this, "有人访问登陆"));
            return "login";
        }
    }

    结果:

    收到事件,消息为:有人访问登陆
    cn.qlq.event.CustomApplicationEvent[source=cn.qlq.controller.system.LoginController@333a44f2]

  • 相关阅读:
    PPT 转 word
    securefx 系统中不到指定文件 (转中文)
    U盘使用技巧篇 制作一般人删除不了的文件(宣传视频) (量产开卡)
    电脑加载有文件的CD、DVD驱动器图标修改
    CentOS 7 网卡注释
    linux IP 注释
    VMware虚拟机安装黑群晖DSM6.2 (转)
    DAS、SAN和NAS三种服务器存储方式 (转)
    wdCP V3.2
    JS异步编程 XHR的用法
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/13267032.html
Copyright © 2011-2022 走看看