事件监听处理小框架

来源:互联网 发布:3g软件培训班 编辑:程序博客网 时间:2024/04/30 21:29

转:http://www.javaeye.com/topic/519498

基于SPRING的事件处理其实很简单,初学者不必一开始就担心搞不懂,本文先是介绍完全基于SPRING的事件监听实现(熟识Spring事件监听的朋友应该跳过第一节),然后依葫芦画瓢自已做一个性能更好事件监听小框架,最后在回贴中加入了注解版
    为何要使用事件监听,而不是直接调用?打个比方,在系统删除一个用户时,需要把用户的其他信息和行为记录一起删除,此时最容易想到的是:删除用户时,调用其他Service类接口删除与之相关的信息和用户行为记录,但是用户是一个基础模块,随着系统模块不断增加,会有越来越多的模块与之关联,按以上方案,每增加一个与用户相关的模块,那么删除用户时就要把这个模块的信息也一起删除,这样用户这个模块就与这些业务模块偶合在一起了,只要系统不断的增加或减少模块,用户模块就永远都改不完。(以上逻辑仅为说明问题)
    如果采用事件监听,那么在删除一个用户时,发布一事件就完事了,所有相关的模块监听此事件,在事件处理中删除本模块对应的数据,那么用户模块也不会与这些模块有任何偶合了。
    好,回到主题
    1、完全基于SPRING的事件监听与处理:
    首先,SERVICE总接口类继承org.springframework.context.ApplicationListener接口,并且在SERVICE的抽象类中实现此接口的方法,SERVICE总接口代码:

Java代码 复制代码
  1. import org.springframework.context.ApplicationListener;   
  2.   
  3. /**  
  4.  * 业务层顶层接口  
  5.  */  
  6. public interface IBaseService extends ApplicationListener{   
  7.   
  8. }  



SERVICE抽象类代码,所有具体业务类皆继承此抽象业务类

Java代码 复制代码
  1. import org.springframework.context.ApplicationEvent;   
  2.   
  3. /**  
  4.  * 实现顶层接口的抽象类  
  5.  */  
  6. public abstract class AbstractBaseService implements IBaseService {   
  7.   
  8.   
  9.     /**  
  10.      * 此方法不允许重写,要监听事件,重写onBaseEvent方法  
  11.      */  
  12.     public final void onApplicationEvent(ApplicationEvent event) {   
  13.         if (event instanceof BaseEvent) {   
  14.             onBaseEvent((BaseEvent) event);   
  15.         }   
  16.     }   
  17.   
  18.     /**  
  19.      * 要监听事件就重写此方法  
  20.      */  
  21.     protected void onBaseEvent(BaseEvent event) {   
  22.     }   
  23.   
  24.          /**  
  25.      * 发布事件  
  26.      */  
  27.     protected final void publishEvent(BaseEvent event) {   
  28.         //取得SPRING容器并发布事件   
  29.         org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext().publishEvent(event);   
  30.     }   
  31. }  


BaseEvent类是我自定义的一个类,各业务模块要发布事件,首先定义自已的EVENT类,继承BaseEvent。BaseEvent类代码

Java代码 复制代码
  1. import org.springframework.context.ApplicationEvent;   
  2.   
  3. public class BaseEvent extends ApplicationEvent {   
  4.   
  5.     public BaseEvent(Object source) {   
  6.         super(source);   
  7.     }   
  8.   
  9. }  



用户业务类UserSerivceImpl删除一个用户时,需要发布事件,所以要先定义一个UserDeleteEvent事件类

Java代码 复制代码
  1. import com.esc.permission.model.User;   
  2.   
  3. public class UserDeleteEvent extends BaseEvent {   
  4.        
  5.     public UserDeleteEvent(User user){   
  6.         super(user);   
  7.     }   
  8.        
  9.     public User getUser(){   
  10.         return (User) super.getSource();   
  11.     }   
  12.   
  13. }  


下面是UserSerivceImpl类删除用户的方法模拟

Java代码 复制代码
  1. public void deleteUser(String id){   
  2.     //先发布删除事件   
  3.     publishEvent(new UserDeleteEvent(new User(id)));   
  4.     //再把用户删除   
  5.     userDao.deleteUser(id);   
  6. }  


假如有一个OtherServiceImpl业务类需要接收用户删除事件并做一些处理,

Java代码 复制代码
  1. public class OtherServiceImpl extends AbstractBaseService {   
  2.     private IBaseDAO otherDao;   
  3.   
  4.     /**  
  5.      * 重写父类的方法,处理用户删除事件  
  6.      */  
  7.     protected void onBaseEvent(BaseEvent baseEvent){   
  8.         if(baseEvent instanceof UserDeleteEvent){//如果是用户删除事件   
  9.             otherDao.deleteOtherData(((User)baseEvent.getSource()).getId());   
  10.         }   
  11.     }   
  12. }  


    到此,所有逻辑就完了,用户业务类中根本不需要知道谁将会处理此事件,所以完全与其它模块解偶了。
    这里用了一个偷懒的方法,那就是在业务类的顶层接口IBaseService中实现了ApplicationListener接口,这样其实所有具体业务类都参与了所有事件的处理,即使是那些根本不需要监听事件的业务类。对于所有根本不需要监听事件的业务类,事件发生时就调用其父类空方法onBaseEvent,这里有一点性能浪费,如果改进?不要在顶层接口IBaseService中实现ApplicationListener接口,而是在具体的需要监听事件的业务类中实现此接口,但是这样还是有一点缺陷,那就是此具体业务类还是会监听到它不关心的事件,不论是用户删除事件还是用户修改事件,没办法,有得必有失,想要方便,肯定会有所牺牲。

    如果只想监听到自已所关心的事件呢,那么就要自已实现一套事件处理的小框架了:

    2、自定义的事件监听与处理框架。
    首先重新定义事件监听器接口,不再使用SPRING提供的ApplicationListener接口:

Java代码 复制代码
  1. import java.util.List;   
  2.   
  3. /**  
  4.  * 事件处理接口,实现此接口并且getEventClasses方法的返回结果条数大于0,方可处理对应的事件  
  5.  */  
  6. public interface IBaseEventListener {   
  7.     /**  
  8.      * 事件处理的方法  
  9.      */  
  10.     public void onBaseEvent(BaseEvent event);   
  11.        
  12.     /**  
  13.      * 所要处理的事件类型  
  14.      */  
  15.     public List<Class<? extends BaseEvent>> getEventClasses();   
  16. }  


    这个接口比SPRING的事件接口多了一个方法。因为SPRING原来做法是只要实现了它提供的接口,任何事件发生时都会得到调用,这种做法是很方便使用的,这里沿用了这种思路。多出的那个方法作用是描述本类处理哪些事件。这时我需要一个类,这个类要在系统启动时把所有实现此接口的业务类找出来,归好类,以便事件发生时进行精确调用,并且提供事件发布事件的静态方法,这样我们才能知道事件在何时发生了。好,下面就来编写这个类

Java代码 复制代码
  1.   
  2. /**  
  3.  * 事件处理相关操作工具类  
  4.  */  
  5. public class EventController {   
  6.     /** KEY:事件类的类名,值:所有监听此事件的处理类实例  */  
  7.     private static Map<String,List<IBaseEventListener>> listeners = new LinkedHashMap<String, List<IBaseEventListener>>();   
  8.        
  9.     private EventController(){   
  10.         //no instance   
  11.     }   
  12.        
  13.     /**  
  14.      * 扫瞄所有bean,进行事件监听(业务类按事件类型归类),此方法要在系统启动完后立即调用  
  15.      * 此方法大概过程是  
  16.      * 1、从SPRING中找出所实现了IBaseEventListener的具体业务类实例  
  17.      * 2、把这些实例归类装进MAP变量listeners中,此MAP变量的结构是:  
  18.      * "UserDeleteEvent.class",{ortherServiceImpl,xxxServiceImpl,...}  
  19.      * "UserUpdateEvent.class",{yyyServiceImpl,zzzServiceImpl,...}  
  20.      * key,valueList  
  21.      */  
  22.     public static void initBaseEventListener(){   
  23.         //从SPRING中得到实现了某接口的业务类   
  24.         Map<String,IBaseEventListener> beans = ContextLoader.getCurrentWebApplicationContext().getBeansOfType(IBaseEventListener.class);   
  25.         if(beans==null || beans.size()==0)   
  26.             return;   
  27.         //下面循环进行归类   
  28.         Collection<IBaseEventListener> services = beans.values();   
  29.         for (IBaseEventListener service : services) {   
  30.             List<Class<? extends BaseEvent>> eventClasses = service.getEventClasses();   
  31.             if(eventClasses==null || eventClasses.size()==0)   
  32.                 continue;   
  33.                
  34.             for (int i = 0; i < eventClasses.size(); i++) {   
  35.                 List<IBaseEventListener> list = listeners.get(eventClasses.get(i).getName());   
  36.                 if(list==null){   
  37.                     list = new ArrayList<IBaseEventListener>();   
  38.                     listeners.put(eventClasses.get(i).getName(), list);   
  39.                     list.add(service);   
  40.                 }else{   
  41.                     if(list.contains(service)){   
  42.                         continue;   
  43.                     }else{   
  44.                         list.add(service);   
  45.                     }   
  46.                 }   
  47.             }   
  48.         }   
  49.     }   
  50.        
  51.     /**  
  52.      * 发布事件的静态方法  
  53.      */  
  54.     public static void publishEvent(BaseEvent event){   
  55.         //根据实际事件名称,从listeners中找出监听了此事件的业务类,调用之   
  56.         List<IBaseEventListener> list = listeners.get(event.getClass().getName());   
  57.         if(list!=null && list.size()>0){   
  58.             for (IBaseEventListener listener : list) {   
  59.                 //此处不能捕捉异常,因为任何一个处理类实例出错都应该全部回滚   
  60.                 listener.onBaseEvent(event);   
  61.             }   
  62.         }   
  63.     }   
  64. }  


    接着重新定义事件类本身,此类也不再继承SPRING的ApplicationEvent类,其它基本还和原来一样。

Java代码 复制代码
  1. public class BaseEvent {   
  2.     private Object source;   
  3.     public BaseEvent() {   
  4.     }   
  5.        
  6.     public BaseEvent(Object source) {   
  7.         this.source = source;   
  8.     }   
  9.   
  10.     public Object getSource() {   
  11.         return source;   
  12.     }   
  13. }  



业务类顶层接改而继承自已的事件接口

Java代码 复制代码
  1. /**    
  2.  * 业务层顶层接口    
  3.  */     
  4. public interface IBaseService extends IBaseEventListener{      
  5.      
  6. }   



在SERVICE的抽象类中去掉onApplicationEvent方法,不需要了,改写发布事件的方法,修改后变成这样

Java代码 复制代码
  1. import org.springframework.context.ApplicationEvent;   
  2.   
  3. /**  
  4.  * 实现顶层接口的抽象类  
  5.  */  
  6. public abstract class AbstractBaseService implements IBaseService {   
  7.          /**  
  8.      * 发布事件  
  9.      */  
  10.     protected final void publishEvent(BaseEvent event) {   
  11.         //使用自已事件工具类发布事件   
  12.         EventController.publishEvent(event);   
  13.     }   
  14.   
  15.          /**    
  16.           * 默认实现处理事件的方法   
  17.             */     
  18.          public void onBaseEvent(BaseEvent event) {      
  19.          }   
  20.   
  21.          /**  
  22.      * 注意虽然也是在顶层接口中都实现了事件监听,但是只有这个方法有返回值时,才真正的会被调用  
  23.      */  
  24.     public List<Class<? extends BaseEvent>> getEventClasses(){   
  25.                    return null;   
  26.          }   
  27.   
  28. }  


    到此框架就完成了,UserServiceImpl删除用户时发布事件的代码还和原来一样,不需要改变,只是监听类要变一下,不再实现ApplicationListener接口,改而实现IBaseEventListener接口,不过不需要再具体类中实现,因为已经在顶层接口中继承了,代码

Java代码 复制代码
  1. public class OtherServiceImpl extends AbstractBaseService {   
  2.     private IBaseDAO otherDao;   
  3.   
  4.     /**  
  5.      * 重写父类的方法,处理用户删除事件  
  6.      */  
  7.     protected void onBaseEvent(BaseEvent baseEvent){   
  8.                   //如果本类只处理一个事件,这里就不需要再类型判断了   
  9.                   UserDeleteEvent event = (UserDeleteEvent)baseEvent;   
  10.         otherDao.deleteOtherData(event.getSource().getId());   
  11.     }   
  12.             
  13.          /**  
  14.      * 返回本类所要处理所有事件  
  15.      */  
  16.     public List<Class<? extends BaseEvent>> getEventClasses() {   
  17.         List<Class<? extends BaseEvent>> eventClasses = new ArrayList<Class<? extends BaseEvent>>(1);   
  18.         //本类只监听用户删除事件   
  19.         eventClasses.add(UserDeleteEvent.class);   
  20.         return eventClasses;   
  21.     }   
  22. }  
  23. 3、注解版实现:
    1、定义注解类
    Java代码 复制代码
    1. import java.lang.annotation.ElementType;   
    2. import java.lang.annotation.Retention;   
    3. import java.lang.annotation.RetentionPolicy;   
    4. import java.lang.annotation.Target;   
    5.   
    6. @Target(ElementType.METHOD)       
    7. @Retention(RetentionPolicy.RUNTIME)   
    8. public @interface HandleEvent {   
    9.     Class<? extends BaseEvent>[] events();   
    10. }  


    2、在顶层接口IBaseService增加一个方法getRealClass,此方法用于返回真正的业务类字节码引用,此方法在抽象业务类用统一实现即可。本来不需要此方法,但由于使用了SPRING的AOP,一时没有找到取得真正业务类字节码引用的方法,所以才定义这么个接口,代码
    Java代码 复制代码
    1. /**  
    2.  * 业务层顶层接口,自定义的小框架里可以在顶层业务接口中直接继承事件接口,不影响性能  
    3.  * 因为在初始化事件监听器时,已经过滤了没有真正实现接口方法的类,所以不会造成多余的调用  
    4.  */  
    5. public interface IBaseService extends IBaseEventListener{   
    6.     public Class<? extends IBaseService> getRealClass();   
    7. }  

    抽象业务类的代码
    Java代码 复制代码
    1. /**  
    2.  * 实现顶层接口的抽象类  
    3.  */  
    4. public abstract class AbstractBaseService implements IBaseService{   
    5.     /**  
    6.      * 发布事件 2008-9-18  
    7.      */  
    8.     protected void publishEvent(BaseEvent event) {   
    9.         EventController.publishEvent2(event);   
    10.     }   
    11.     //具体子类中不需要再实现   
    12.     public Class<? extends IBaseService> getRealClass(){   
    13.         return this.getClass();   
    14.     }   
    15.        
    16.     public void onBaseEvent(BaseEvent event){   
    17.         //这里空实现,且没有注解,这样,如果具体业务类没有重写方法,   
    18.         //初始化事件监听器时就会被过滤掉,不会造成多余调用   
    19.     }   
    20. }  


    改写IBaseEventListener接口,删除getEventClasses方法,因为使用注解来定义要处理事件,所以此方法不再需要。本来onBaseEvent方法也可以去除,直接由注解来定义即可,但由于以下原因,还是要保留:1、如果没有任何接口定义onBaseEvent方法,那么SPRING的代理类也不会有此方法,这样就无法使用AOP的种种好处了;2、为规范事件处理的方法名和参数,更易于后续维护,所以还是要有个接口定义为好。
    Java代码 复制代码
    1. /**  
    2.  * 事件处理接口,实现此接口并且getEventClasses方法的返回结果条数大于0,方可处理对应的事件  
    3.  */  
    4. public interface IBaseEventListener {   
    5.     /**  
    6.      * 事件处理的方法  
    7.      */  
    8.     public void onBaseEvent(BaseEvent event);   
    9. }  


    事件处理工具类
    Java代码 复制代码
    1. /**  
    2.  * 事件处理相关操作工具类  
    3.  */  
    4. public class EventController {   
    5.     private static Map<String,List<LisenerInfo>> listeners2 = new LinkedHashMap<String, List<LisenerInfo>>();   
    6.     /**  
    7.      * 扫瞄所有bean,进行事件监听  
    8.      */  
    9.     public static void initBaseEventListener2(){   
    10.         //取得所有业务类   
    11.         Map<String,IBaseService> beans = SysContext.getBeansOfType(IBaseService.class);   
    12.         if(beans==null || beans.size()==0)   
    13.             return;   
    14.         Collection<IBaseService> values = beans.values();   
    15.         for (IBaseService listener : values) {   
    16.             //注意这里不能使用listener.getClass()方法,因此方法返回的只是SPRING的代理类,此代理类的方法没有注解信息   
    17.             Method[] methods = listener.getRealClass().getDeclaredMethods();   
    18.             for (Method method : methods) {   
    19.                  //判断方法中是否有指定注解类型的注解      
    20.                 boolean hasAnnotation = method.isAnnotationPresent(HandleEvent.class);       
    21.                 if (hasAnnotation) {   
    22.                     //根据注解类型返回方法的指定类型注解      
    23.                     HandleEvent annotation = method.getAnnotation(HandleEvent.class);       
    24.                     Class<? extends BaseEvent>[] events = annotation.events();   
    25.                     if(events==null || events.length==0){//这里过滤掉没有真正实现事件监听的业务类   
    26.                         continue;   
    27.                     }   
    28.                     for (int i = 0; i < events.length; i++) {   
    29.                         try {   
    30.                             if(listeners2.containsKey(events[i].getName())){   
    31.                                 //注意这里要用代理类的方法,即listener.getClass().getMethod(method.getName()),不能直接使用method变量,下同   
    32.                                 listeners2.get(events[i].getName()).add(new LisenerInfo(listener,listener.getClass().getMethod(method.getName())));   
    33.                             }else{   
    34.                                 listeners2.put(events[i].getName(),Arrays.asList(new LisenerInfo[]{new LisenerInfo(listener,listener.getClass().getMethod(method.getName()))}));   
    35.                             }   
    36.                         } catch (Exception e) {   
    37.                             throw new UnknowException("初始化事件监听器时出错:",e);   
    38.                         }   
    39.                     }   
    40.                 }   
    41.             }   
    42.         }   
    43.     }   
    44.     /**  
    45.      * 发布事件  
    46.      */  
    47.     public static void publishEvent2(BaseEvent event){   
    48.         List<LisenerInfo> list = listeners2.get(event.getClass().getName());   
    49.         if(list!=null && list.size()>0){   
    50.             for (LisenerInfo listener : list) {   
    51.                 try {   
    52.                     listener.getMethod().invoke(listener.getService(), event);   
    53.                 } catch (Exception e) {   
    54.                     //此处不能捕捉异常,因为任何一个处理类实例出错都应该全部回滚   
    55.                     throw new UnknowException(e);   
    56.                 }    
    57.             }   
    58.         }   
    59.     }   
    60. }   
    61.   
    62. //此类记录目标方法和目标类   
    63. class LisenerInfo{   
    64.     private Method method;//目标方法   
    65.     private Object service;//业务类实例   
    66.     public LisenerInfo(Object service,Method method){   
    67.         this.method = method;   
    68.         this.service = service;   
    69.     }   
    70.     public Method getMethod() {   
    71.         return method;   
    72.     }   
    73.     public Object getService() {   
    74.         return service;   
    75.     }   
    76. }  


    好了,框架完成,事件发布还和以前那样,来看看事件处理的实现,同样也不再需要getEventClasses方法了
    Java代码 复制代码
    1. //不再需要每个具体业务都实现IBaseEventListener接口   
    2. public class OtherServiceImpl extends AbstractBaseService implements OtherService{      
    3.     private IBaseDAO otherDao;      
    4.      
    5.     /**    
    6.      * 重写父类的方法,处理用户删除事件    
    7.      */  
    8.     @HandleEvent(events={UserDeleteEvent.class,UserUpdateEvent.class})   
    9.     public void onBaseEvent(BaseEvent baseEvent){      
    10.         if(baseEvent instanceof UserDeleteEvent){//如果是用户删除事件      
    11.             otherDao.deleteOtherData(((User)baseEvent.getSource()).getId());      
    12.         }else{   
    13.             //....   
    14.         }   
    15.     }      
    16. }