zoukankan      html  css  js  c++  java
  • 自定义事件驱动加异步执行

    1.编写自定义注解 EventListener 主要用来标识是否异步调用
    2.实现事件监听器 implements ApplicationListener<ApplicationEvent>,扫描指定包指定注解方法,调用事件监听方法
    3.编写自定义事件 CustomEvent
    4.配置线程池
    5.编写事件监听 CustomListener
    6.测试

    1.编写自定义注解 EventListener 主要用来标识是否异步调用

    package com.yun.base.event.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /** 
     * 自定事件监听标签
     * 
     */
    @Target( ElementType.METHOD )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface EventListener {
        /**
         * 是否异步
         * @return boolean <b>default:false</b>
         */
        boolean Async() default false;
    }


    2.实现事件监听器 implements ApplicationListener<ApplicationEvent>,扫描指定包指定注解方法,调用事件监听方法

    package com.yun.base.event.listener;
    
    import java.lang.reflect.Type;
    import java.util.Iterator;
    import java.util.Set;
    
    import javax.annotation.Resource;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    
    import com.yun.base.event.EventList;
    import com.yun.base.event.scanner.EventPackageScanner;
    
    @Component
    public class EventCommonListener implements
            ApplicationListener<ApplicationEvent> {
    
        private static final Log log = LogFactory.getLog(EventCommonListener.class);
        @Resource
        private AsyncThread asyncThread;
    
        public AsyncThread getAsyncThread() {
            return asyncThread;
        }
    
        public void setAsyncThread(AsyncThread asyncThread) {
            this.asyncThread = asyncThread;
        }
    
        /**
         * 从资源文件中读取事件扫描包路径 配置名:event.scanner_path
         */
        @Value("${event.scanner_path}")
        private String _ScannerPath;
    
        public void onApplicationEvent(final ApplicationEvent event) {
            // 整体思路:通过读取配置项,扫描指定包路径下,包含有自定义注解的方法类,并执行
    
            // 1.从配置中获取要扫描的包路径
            if (!StringUtils.isEmpty(_ScannerPath)) {
                // 2.扫描指定包下面的类且含有自定义注解的方法
                Set<EventList> se = EventPackageScanner
                        .GetEventListenerByPath(_ScannerPath);
    
                // 3.循环处理自定义事件类
                Iterator<?> iter = se.iterator();
                while (iter.hasNext()) {
                    EventList eventList = (EventList) iter.next();
                    Type paramType = eventList.getParamType();
                    Class<?> cls = (Class<?>) paramType;
                    // 参数类型一致,则执行,否则跳过
                    boolean flag = compareClass(cls, event.getClass());
                    if (flag) {
                        // 执行这部分方法
                        if (eventList.isAsync()) {
                            // 异步执行
                            asyncThread.runMethodAsync(eventList, event);
                        } else {
                            // 同步执行
                            asyncThread.runMethod(eventList, event);
                        }
                    }
                }
            } else {
                log.warn("EventCommonListener==> EventListener Scanner Path Not Configured!!未配置事件包扫描路径");
            }
        }
    
        /**
         * 比较两个类是否一致
         * 
         * @param clsZ
         * @param clsF
         * @return
         */
        private boolean compareClass(Class<?> clsZ, Class<?> clsF) {
            String clsZS = clsZ.getName();
            String clsFS = clsF.getName();
            if (clsZS.equals(clsFS)) {
                return true;
            }
            return false;
        }
    }

    类扫描工具类 EventPackageScanner

    package com.yun.base.event.scanner;
    
    import java.io.File;
    import java.io.FileFilter;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.net.JarURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.Enumeration;
    import java.util.LinkedHashSet;
    import java.util.Set;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.util.StringUtils;
    
    import com.yun.base.event.EventList;
    import com.yun.base.event.annotation.EventListener;
    
    /**
     * 获取指定目录下指定自定义注解(EventListener)的类和方法
     * 
     */
    public class EventPackageScanner {
    
        private static final Log log = LogFactory.getLog( EventPackageScanner.class );
    
        private static final char WIN_PATH_SEPARATE = '/';
    
        private static final char LINUX_PATH_SEPARATE = '.';
    
        /**
         * 获取指定目录下指定自定义注解(EventListener)的类和方法
         * 
         * @param packageName
         * @return Set<EventList>
         */
        public static Set<EventList> GetEventListenerByPath( String packageName ) {
            Set<Class<?>> classes = getClasses( packageName );
            Set<EventList> eventListResult = new LinkedHashSet<EventList>();
            if( StringUtils.isEmpty( classes ) ) {
                log.warn( "EventCommonListener==>GetEventListenerByPath: Class not found!" );
            } else {
                for( Class<?> clazz : classes ) {
                    eventListResult.addAll( getReflectAllMethod( clazz ) );
                }
            }
            return eventListResult;
        }
    
        /**
         * 从包package中获取所有的Class
         * 
         * @param packName 包路径
         * @return Set<Class<?>> 所有的Class集合
         */
        private static Set<Class<?>> getClasses( String packName ) {
            // 第一个class类的集合
            Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
            // 是否循环迭代
            boolean recursive = true;
            // 获取包的名字 并进行替换
            String packageName = packName;
            String packageDirName = packageName.replace( EventPackageScanner.LINUX_PATH_SEPARATE, EventPackageScanner.WIN_PATH_SEPARATE );
            // 定义一个枚举的集合 并进行循环来处理这个目录下的things
            Enumeration<URL> dirs;
            try {
                dirs = Thread.currentThread().getContextClassLoader().getResources( packageDirName );
                // 循环迭代下去
                while( dirs.hasMoreElements() ) {
                    // 获取下一个元素
                    URL url = dirs.nextElement();
                    // 得到协议的名称
                    String protocol = url.getProtocol();
                    // 如果是以文件的形式保存在服务器上
                    if( "file".equals( protocol ) ) {
                        // 获取包的物理路径
                        String filePath = URLDecoder.decode( url.getFile(), "UTF-8" );
                        // 以文件的方式扫描整个包下的文件 并添加到集合中
                        findAndAddClassesInPackageByFile( packageName, filePath, recursive, classes );
                    } else if( "jar".equals( protocol ) ) {
                        // 如果是jar包文件
                        // 定义一个JarFile
                        JarFile jar;
                        try {
                            // 获取jar
                            jar = ( ( JarURLConnection )url.openConnection() ).getJarFile();
                            // 从此jar包 得到一个枚举类
                            Enumeration<JarEntry> entries = jar.entries();
                            // 同样的进行循环迭代
                            while( entries.hasMoreElements() ) {
                                // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                                JarEntry entry = entries.nextElement();
                                String name = entry.getName();
                                // 如果是以"/"开头的
                                if( name.charAt( 0 ) == EventPackageScanner.WIN_PATH_SEPARATE ) {
                                    // 获取"/"后面的字符串
                                    name = name.substring( 1 );
                                }
                                // 如果前半部分和定义的包名相同
                                if( name.startsWith( packageDirName ) ) {
                                    int idx = name.lastIndexOf( EventPackageScanner.WIN_PATH_SEPARATE );
                                    // 如果以"/"结尾 是一个包
                                    if( idx != -1 ) {
                                        // 获取包名 把"/"替换成"."
                                        packageName = name.substring( 0, idx ).replace( EventPackageScanner.WIN_PATH_SEPARATE, EventPackageScanner.LINUX_PATH_SEPARATE );
                                    }
                                    // 如果可以迭代下去 并且是一个包
                                    if( ( idx != -1 ) || recursive ) {
                                        // 如果是一个.class文件 而且不是目录
                                        if( name.endsWith( ".class" ) && !entry.isDirectory() ) {
                                            // 去掉后面的".class" 获取真正的类名
                                            String className = name.substring( packageName.length() + 1, name.length() - 6 );
                                            try {
                                                // 添加到classes
                                                classes.add( Class.forName( packageName + '.' + className ) );
                                            } catch( ClassNotFoundException e ) {
                                                log.error( "EventCommonListener==>getClasses: ClassNotFound" + e.toString() );
                                            }
                                        }
                                    }
                                }
                            }
                        } catch( IOException e ) {
                            log.error( "EventCommonListener==>getClasses: 在扫描用户定义视图时从jar包获取文件出错:" + e.toString() );
                        }
                    }
                }
            } catch( IOException e ) {
                log.error( "EventCommonListener==>getClasses: The IO exception is scanned under the user specified path!!在扫描用户指定路下时IO异常:" + e.toString() );
            }
            return classes;
        }
    
        /**
         * 以文件的形式来获取包下的所有Class
         *
         * @param packageName
         * @param packagePath
         * @param recursive
         * @param classes
         */
        private static void findAndAddClassesInPackageByFile( String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes ) {
            // 获取此包的目录 建立一个File
            File dir = new File( packagePath );
            // 如果不存在或者 也不是目录就直接返回
            if( !dir.exists() || !dir.isDirectory() ) {
                // System.out.println("用户定义包名 " + packageName + " 下没有任何文件");
                return;
            }
            // 如果存在 就获取包下的所有文件 包括目录
            File[] dirfiles = dir.listFiles( new FileFilter() {
    
                // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
                public boolean accept( File file ) {
                    return ( recursive && file.isDirectory() ) || ( file.getName().endsWith( ".class" ) );
                }
            } );
            // 循环所有文件
            for( File file : dirfiles ) {
                // 如果是目录 则继续扫描
                if( file.isDirectory() ) {
                    findAndAddClassesInPackageByFile( packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes );
                } else {
                    // 如果是java类文件 去掉后面的.class 只留下类名
                    String className = file.getName().substring( 0, file.getName().length() - 6 );
                    try {
                        // 添加到集合中去
                        // classes.add(Class.forName(packageName + '.' + className));
                        // 这里如用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                        classes.add( Thread.currentThread().getContextClassLoader().loadClass( packageName + '.' + className ) );
                    } catch( ClassNotFoundException e ) {
                        log.error( "EventCommonListener==>findAndAddClassesInPackageByFile 添加用户自定义视图类错误 找不到此类的.class文件" + e.toString() );
                    }
                }
            }
        }
    
        /**
         * 取类中含有自定义注解EventListener的方法
         * 
         * @param mLocalClass 传入要检索的类
         * @return Set<EventList> 返回EventList集合
         */
        private static Set<EventList> getReflectAllMethod( Class<?> mLocalClass ) {
            Set<EventList> eventListResult = new LinkedHashSet<EventList>();
            Class<?> cls = mLocalClass;
            boolean flag = false;// 同步异步标识
            do {
                Method methods[] = cls.getDeclaredMethods(); // 取得全部的方法
                for( Method method : methods ) {
                    if( !StringUtils.isEmpty( method ) ) {
                        // 判断是否含有自定义注解EventListener,有则记录
                        EventListener el = method.getAnnotation( EventListener.class );
                        if( !StringUtils.isEmpty( el ) ) {
                            // 自定义事件只认1个Event参数,其他方法先忽略
                            if( method.getGenericParameterTypes().length == 1 ) {
                                // 同步异步分类,判断是否含有注解Async,有则设置异步标识为true
                                Async async = method.getAnnotation( Async.class );
                                if( !StringUtils.isEmpty( async ) ) {
                                    flag = true;
                                } else {
                                    flag = el.Async();
                                }
                                // 自定义事件类EventList
                                EventList eventList = new EventList();
                                eventList.setCls( cls );
                                eventList.setMethod( method );
                                eventList.setParamType( method.getGenericParameterTypes()[ 0 ] );
                                eventList.setAsync( flag );
                                eventListResult.add( eventList );
                            }
                        }
                    }
                }
                cls = cls.getSuperclass();
            } while( !StringUtils.isEmpty( cls ) );
    
            return eventListResult;
        }
    }

    反射执行异步方法

    package com.yun.base.event.listener;
    
    import java.lang.reflect.InvocationTargetException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    import com.yun.base.event.EventList;
    
    
    @Component
    public class AsyncThread {
        
        private static final Log log = LogFactory.getLog( AsyncThread.class );
    
        /**
         * 异常执行方法
         * @param eventList
         * @param event
         */
        @Async
        public void runMethodAsync( EventList eventList, final ApplicationEvent event ) {
            try {
                eventList.getMethod().invoke( eventList.getCls().newInstance(), event );
            } catch( IllegalAccessException e ) {
                log.error( "EventCommonListener==>runMethod IllegalAccessException ERROR:" + e.toString() );
            } catch( IllegalArgumentException e ) {
                log.error( "EventCommonListener==>runMethod IllegalArgumentException ERROR:" + e.toString() );
            } catch( InvocationTargetException e ) {
                log.error( "EventCommonListener==>runMethod InvocationTargetException ERROR:" + e.toString() );
            } catch( InstantiationException e ) {
                log.error( "EventCommonListener==>runMethod InstantiationException ERROR:" + e.toString() );
            }
        }
    
        /**
         * 执行方法(默认同步)
         * @param eventList
         * @param event
         */
        public void runMethod( EventList eventList, final ApplicationEvent event ) {
            try {
                eventList.getMethod().invoke( eventList.getCls().newInstance(), event );
            } catch( IllegalAccessException e ) {
                log.error( "EventCommonListener==>runMethod IllegalAccessException ERROR:" + e.toString() );
            } catch( IllegalArgumentException e ) {
                log.error( "EventCommonListener==>runMethod IllegalArgumentException ERROR:" + e.toString() );
            } catch( InvocationTargetException e ) {
                log.error( "EventCommonListener==>runMethod InvocationTargetException ERROR:" + e.toString() );
            } catch( InstantiationException e ) {
                log.error( "EventCommonListener==>runMethod InstantiationException ERROR:" + e.toString() );
            }
        }
    
    }

    BO

    package com.yun.base.event;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Type;
    
    /**
     * 自定义事件类
     *
     */
    public class EventList implements java.io.Serializable {
    
        private static final long serialVersionUID = -7011434720946720528L;
    
        //
        private Class<?> cls;
    
        // 方法
        private Method method;
    
        // 参数类型
        private Type paramType;
    
        // 是否同步
        private boolean isAsync;
    
        public Class<?> getCls() {
            return cls;
        }
    
        /**
         * 设置自定义事件类
         * @param cls
         */
        public void setCls( Class<?> cls ) {
            this.cls = cls;
        }
    
        public Method getMethod() {
            return method;
        }
    
        /**
         * 设置自定义事件方法
         * @param method
         */
        public void setMethod( Method method ) {
            this.method = method;
        }
    
        public Type getParamType() {
            return paramType;
        }
    
        /**
         * 设置自定义事件方法参数类型
         * @param paramType
         */
        public void setParamType( Type paramType ) {
            this.paramType = paramType;
        }
    
        public boolean isAsync() {
            return isAsync;
        }
    
        /**
         * 设置自定义事件同步异步类型
         * @param isAsync
         */
        public void setAsync( boolean isAsync ) {
            this.isAsync = isAsync;
        }
    
        @Override
        public String toString() {
            return "EventList [Class=" + cls.toString() + ", Method=" + method.toString() + ", ParamentType=" + paramType.toString() + ", IsAsync=" + isAsync + "]";
        }
    }

    3.编写自定义事件 CustomEvent

    package com.yun.base.event;
    
    import org.springframework.context.ApplicationEvent;
    
    import com.yun.base.event.bean.Person;
    
    public class CustomEvent extends ApplicationEvent {
    
        public CustomEvent(Person person) {
            super(person);
        }
    
    }


    4.配置线程池 beans.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task"
        xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
        default-lazy-init="true">
    
        <context:component-scan base-package="com.yun.base.event" />
    
        <!-- 任务调度器 -->
        <task:scheduler id="scheduler" pool-size="${event.task_scheduler.poolSize}" />
    
        <!-- 任务执行器 -->
        <task:executor id="executor" pool-size="${event.task_executor.poolSize}" />
    
        <!--开启注解调度支持 @Async @Scheduled -->
        <task:annotation-driven executor="executor"
            scheduler="scheduler" proxy-target-class="true" />
        
    
    </beans>


    5.编写事件监听 CustomListener

    package com.yun.base.event.listener;
    
    import com.yun.base.event.CustomEvent;
    import com.yun.base.event.annotation.EventListener;
    import com.yun.base.event.bean.Person;
    
    public class CustomListener {
    
        @EventListener(Async=true)
        public void listener1( CustomEvent event ) {
            try {
                Thread.currentThread().sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Person p = (Person)event.getSource();
            System.out.println(p.getName());
            System.out.println( "MyAnnotationListener-->listener1:" + Thread.currentThread().getName() );
        }
        @EventListener
        public void listener4( CustomEvent event ) {
            System.out.println( "MyAnnotationListener-->listener4:" + Thread.currentThread().getName() );
        }
        @EventListener
        public void listener5( CustomEvent event ) {
            System.out.println( "MyAnnotationListener-->listener5:" + Thread.currentThread().getName() );
        }
    
        @EventListener
        public void listener2( CustomEvent event ) {
            System.out.println( "MyAnnotationListener-->listener2:" + Thread.currentThread().getName() );
        }
    
        @EventListener( Async = true )
        public void listener3( CustomEvent event ) {
            System.out.println( "MyAnnotationListener-->listener3:" + Thread.currentThread().getName() );
        }
        
    
    }


    6.测试

    properties 配置

    #event settings
    event.scanner_path=com.yun.base.event.listener
    event.task_scheduler.poolSize=2
    event.task_executor.poolSize=2

    spring 配置

    <!-- 加载spring 配置文件 -->
        <bean id="propertyConfigurer" 
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath*:config.properties</value>
                </list>
            </property>
            <property name="ignoreResourceNotFound" value="true"></property>
        </bean>
    
    <import resource="classpath:conf/beans.xml" />
    package test.war;
    
    import java.util.Set;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.yun.base.event.CustomEvent;
    import com.yun.base.event.EventList;
    import com.yun.base.event.bean.Person;
    import com.yun.base.event.scanner.EventPackageScanner;
    
    @RunWith( SpringJUnit4ClassRunner.class )
    @ContextConfiguration( locations = { "classpath:applicationContext.xml" } )
    public class JunitTest {
        
        private static final Logger LOGGER = LoggerFactory.getLogger(JunitTest.class);
        
        @Autowired
        private ApplicationContext applicationContext;
        
        @Test
        public void testPublishEvent() {
            System.out.println( "==================== 发布CustomEvent事件 START ====================" );
            Person p = new Person();
            p.setName("zhangsan");
            applicationContext.publishEvent(new CustomEvent(p));
            System.out.println( "==================== 发布CustomEvent事件 END ====================" );
            
            try {
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
        
        public static void main(String[] args) {
            
            Set<EventList> events = EventPackageScanner.GetEventListenerByPath("com.yun.base.event.listener");
            System.out.println(events.size());
        }
    
        
    }

    结果如下:

    ==================== 发布CustomEvent事件 START ====================
    2017-10-11 15:01:37.193 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'eventCommonListener'
    MyAnnotationListener-->listener4:main
    MyAnnotationListener-->listener5:main
    MyAnnotationListener-->listener2:main
    ==================== 发布CustomEvent事件 END ====================
    MyAnnotationListener-->listener3:executor-1
    zhangsan
    MyAnnotationListener-->listener1:executor-2
    listener3,listener1 为异步执行。2个线程。
  • 相关阅读:
    利用JasperReport+iReport进行Web报表开发
    EEPlat PaaS VS Saleforce force.com
    Python用subprocess的Popen来调用系统命令
    最短路径A*算法原理及java代码实现(看不懂是我的失败)
    Java抓取网页数据(原网页+Javascript返回数据)
    Atitit.dwr3 不能显示错误具体信息的解决方式,控件显示错误具体信息的解决方式 java .net php
    白话经典算法系列之五 归并排序的实现
    poj 百练 2765 八进制小数(精度问题)
    winzip15.0注冊码
    Python:渗透测试开源项目
  • 原文地址:https://www.cnblogs.com/yun965861480/p/7650771.html
Copyright © 2011-2022 走看看