信息化建设 网站,新品发布会的作用,台州椒江区建设局网站,手机网站框架一、背景
在开发工作中#xff0c;我们常常会遇到这样一种情况#xff1a;完成一项任务后#xff0c;需要向其他模块广播消息或通知#xff0c;以触发其他事件的处理。逐个发送请求固然可行#xff0c;但更好的方式是采用事件监听#xff0c;它是设计模式中的发布-订阅模…一、背景
在开发工作中我们常常会遇到这样一种情况完成一项任务后需要向其他模块广播消息或通知以触发其他事件的处理。逐个发送请求固然可行但更好的方式是采用事件监听它是设计模式中的发布-订阅模式和观察者模式的一种实现。
观察者模式简单来说就是一种角色扮演你在做某件事时有人在一旁观察你。当这个人观察到你有兴趣的特定事件发生时他们会根据这个事件做一些其他的事情。但请注意任何想要观察你的人都必须先到你这儿进行登记否则你将无法通知到他们或者他们没有资格来观察你。
在Spring容器中我们可以监听并触发各种事件。通常有两种方法可以实现这一目标使用ApplicationListener接口和使用EventListener注解。这两种方法都能帮助我们更好地管理和响应应用程序中的各种事件。
二、简介
2.1 用途
为了实现一个能够监听应用程序事件的监听器方法我们可以使用特定的注解来标记该方法并定义其支持的事件类型。
如果监听器方法只支持单一的事件类型我们可以将其参数声明为该事件类型的唯一实例。例如如果我们的监听器方法只监听ApplicationEvent实例则可以将其参数声明为ApplicationEvent类型。
如果监听器方法支持多种事件类型我们可以使用注解的classes属性来指定一个或多个支持的事件类型。这样该方法就可以监听到在classes属性中指定的任何事件类型。
2.2 事件处理条件
可以通过 condition 属性指定一个SpEL表达式如果返回 “true”, “on”, “yes”, or “1” 中的任意一个则事件会被处理否则不会。
2.3 处理器
在Spring框架中EventListener注解的处理是通过内部的EventListenerMethodProcessor类进行的。这个类负责寻找带有EventListener注解的方法并在适当的时候触发它们。
当使用Java配置时EventListenerMethodProcessor会自动注册到Spring容器中。你只需要在带有EventListener注解的方法上添加Component或Service等注解Spring就会自动检测并注册这个方法。
2.4 返回值
被标注的方法可以没有返回值也可以有返回值。当有返回值是其返回值会被当作为一个新的事件发送。如果返回类型是数组或集合那么数组或集合中的每个元素都作为一个新的单独事件被发送。
2.5 异常处理
同步监听器抛出的所有checked异常都会被封装成 UndeclaredThrowableException 因为事件发布者只能处理运行时异常unchecked异常。
2.6 异步监听器
当需要异步处理监听器时可以在监听器方法上再增加另外的一个Spring注解 Async但需要注意以下限制
监听器报错不会传递给事件发起者因为双方已经不在同一个线程了。 异步监听器的非空返回值不会被当作新的事件发布。如果需要发布新事件需要注入 ApplicationEventPublisher后手动发布。
2.7 监听器排序
如果同一个事件可能会被多个监听器监听处理那么我们可以使用 Order 注解对各个监听器进行排序。
2.8 源码
Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface EventListener {/*** Alias for {link #classes}.*/AliasFor(classes)Class?[] value() default {};/*** 可以处理的事件类型*/AliasFor(value)Class?[] classes() default {};/*** SpEL表达式判断是否满足处理条件*/String condition() default ;/*** 可以给监听器指定一个id默认是方法的全限定名如mypackage.MyClass.myMethod()*/String id() default ;}三、示例详解
3.1 单一事件监听器
发布事件
Service
public class EventPublisher {private ApplicationEventPublisher eventPublisher;Autowiredpublic void setEventPublisher(ApplicationEventPublisher eventPublisher) {this.eventPublisher eventPublisher;}public void publishPersonSaveEvent(){PersonSaveEvent saveEvent new PersonSaveEvent();saveEvent.setId(1);saveEvent.setName(i余数);saveEvent.setAge(18);eventPublisher.publishEvent(saveEvent);}
}监听事件
Slf4j
Service
public class EventListenerService {EventListenerpublic void handleForPersonSaveEvent(PersonSaveEvent saveEvent){log.info(saveEvent - {}, saveEvent);}
}结果验证
saveEvent - PersonSaveEvent(id1, namei余数, age18)3.2 使用classes实现多事件监听器
发布事件 在上一个示例的基础上再多加一个PersonUpdateEvent事件。
public void publishPersonUpdateEvent(){PersonUpdateEvent updateEvent new PersonUpdateEvent();updateEvent.setId(1);updateEvent.setName(i余数);updateEvent.setAge(19);eventPublisher.publishEvent(updateEvent);
}监听事件
EventListener(classes {PersonSaveEvent.class, PersonUpdateEvent.class})
public void handleForPersonSaveAndUpdateEvent(Object event){log.info(multi handle event - {}, event);
}验证结果 可以监听到多个事件
multi handle event - PersonSaveEvent(id1, namei余数, age18)
multi handle event - PersonUpdateEvent(id1, namei余数, age19)3.3 使用condition筛选监听的事件
发布事件
public void publishPersonSaveEvent(){PersonSaveEvent saveEvent new PersonSaveEvent();saveEvent.setId(1);saveEvent.setName(i余数);saveEvent.setAge(18);eventPublisher.publishEvent(saveEvent);PersonSaveEvent saveEvent2 new PersonSaveEvent();saveEvent2.setId(2);saveEvent2.setName(i余数);saveEvent2.setAge(18);eventPublisher.publishEvent(saveEvent2);
}监听事件
EventListener(condition #root.event.getPayload().getId() 1)
public void handleByCondition(PersonSaveEvent saveEvent){log.info(只处理id等于1的 - {}, saveEvent);
}结果验证 id为2的事件不满足条件所以不会执行。
只处理id等于1的 - PersonSaveEvent(id1, namei余数, age18)3.4 有返回值的监听器
3.4.1 返回一个单一对象
发布事件
public void publishPersonSaveEvent(){PersonSaveEvent saveEvent new PersonSaveEvent();saveEvent.setId(1);saveEvent.setName(i余数);saveEvent.setAge(18);eventPublisher.publishEvent(saveEvent);
}监听事件
EventListener
public void handleForPersonUpdateEvent(PersonUpdateEvent updateEvent){log.info(handle update event - {}, updateEvent);
}EventListener
public PersonUpdateEvent handleHaveReturn(PersonSaveEvent saveEvent){log.info(handle save event - {}, saveEvent);PersonUpdateEvent updateEvent new PersonUpdateEvent();updateEvent.setId(saveEvent.getId());updateEvent.setName(saveEvent.getName());updateEvent.setAge(saveEvent.getAge());return updateEvent;
}验证结果 可以看到我们监听到了2个事件PersonSaveEvent是我们主动发布的事件PersonUpdateEvent 是 handleHaveReturn 方法的返回值会被 Spring 自动当作一个事件被发送。
handle save event - PersonSaveEvent(id1, namei余数, age18)
handle update event - PersonUpdateEvent(id1, namei余数, age18)3.4.2 返回一个集合
将监听器稍作修改使其返回一个集合。
EventListener
public ListPersonUpdateEvent handleHaveReturn(PersonSaveEvent saveEvent){log.info(handle save event - {}, saveEvent);ListPersonUpdateEvent events new ArrayList();PersonUpdateEvent updateEvent new PersonUpdateEvent();updateEvent.setId(saveEvent.getId());updateEvent.setName(saveEvent.getName());updateEvent.setAge(saveEvent.getAge());events.add(updateEvent);PersonUpdateEvent updateEvent2 new PersonUpdateEvent();BeanUtils.copyProperties(updateEvent, updateEvent2);events.add(updateEvent2);return events;}查看结果可以发现集合中的每个对象都被当作一个单独的事件进行发送。
handle save event - PersonSaveEvent(id1, namei余数, age18)
handle update event - PersonUpdateEvent(id1, namei余数, age18)
handle update event - PersonUpdateEvent(id1, namei余数, age18)3.4.3 返回一个数组
和返回值为集合一样数组中的每个对象都被当作一个单独的事件进行发送。
3.4.4 异步监听器
创建两个监听器一个同步一个异步异步监听器就是在方法上加一个 Async 标签即可你可以指定线程池。
EventListener
public void handleForPersonSaveEvent(PersonSaveEvent saveEvent){log.info(handle event - {}, saveEvent);
}Async
EventListener
public void handleForPersonSaveEventAsync(PersonSaveEvent saveEvent){log.info(async handle event - {}, saveEvent);
}从执行结果可以看出异步线程是 task-1不是主线程 main即异步是生效的。
INFO 3851 --- [ main] i.k.s.e.listener.EventListenerService : handle event - PersonSaveEvent(id1, namei余数, age18)
INFO 3851 --- [ task-1] i.k.s.e.listener.EventListenerService : async handle event - PersonSaveEvent(id1, namei余数, age18)3.5 监听器异常处理
3.5.1 同步异常处理
使用 SimpleApplicationEventMulticaster 处理同步监听器抛出异常。 先定义一个ErrorHandler:
Slf4j
Component
public class MyErrorHandler implements ErrorHandler {Overridepublic void handleError(Throwable t) {log.info(handle error - {}, t.getClass());}
}将自定义ErrorHandler绑定到 SimpleApplicationEventMulticaster。
Slf4j
Service
public class EventListenerService {Autowiredprivate SimpleApplicationEventMulticaster simpleApplicationEventMulticaster;Autowiredprivate MyErrorHandler errorHandler;PostConstructpublic void init(){simpleApplicationEventMulticaster.setErrorHandler(errorHandler);}Order(1)EventListenerpublic void handleForPersonSaveEvent(PersonSaveEvent saveEvent) throws AuthException {log.info(handle event - {}, saveEvent);throw new AuthException(test exception);}
} 结果可以看到捕获的异常是 UndeclaredThrowableException。
handle event - PersonSaveEvent(id1, namei余数, age18)
handle error - class java.lang.reflect.UndeclaredThrowableException3.5.2 异步异常处理
使用 SimpleAsyncUncaughtExceptionHandler 来处理 Async 抛出的异常。
Configuration
public class AsyncConfig implements AsyncConfigurer {Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new SimpleAsyncUncaughtExceptionHandler();}
}监听器代码人为的抛出一个异常。
Async
EventListener
public void handleForPersonSaveEvent(PersonSaveEvent saveEvent) throws AuthException {log.info(handle event - {}, saveEvent);throw new AuthException(test exception);
}结果: SimpleAsyncUncaughtExceptionHandler捕获到了 Async 方法抛出的异常 INFO 4416 --- [ task-1] i.k.s.e.listener.EventListenerService : handle event - PersonSaveEvent(id1, namei余数, age18)
ERROR 4416 --- [ task-1] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected exception occurred invoking async method: public void xxxx.handleForPersonSaveEvent(xxxx.PersonSaveEvent) throws javax.security.auth.message.AuthException3.6 监听器排序
如果同时有多个监听器监听同一个事件默认情况下监听器的执行顺序是随机的如果想要他们按照某种顺序执行可以借助Spring的另外一个注解 Order 实现。
创建三个监听器并使用Order 排好序。
Order(1)
EventListener
public void handleForPersonSaveEvent(PersonSaveEvent saveEvent){log.info(handle event1 - {}, saveEvent);
}Order(2)
EventListener
public void handleForPersonSaveEvent2(PersonSaveEvent saveEvent){log.info(handle event2 - {}, saveEvent);
}Order(3)
EventListener
public void handleForPersonSaveEvent3(PersonSaveEvent saveEvent){log.info(handle event3 - {}, saveEvent);
}从执行结果可以看到确实是按照Order中指定的顺序执行的。
handle event1 - PersonSaveEvent(id1, namei余数, age18)
handle event2 - PersonSaveEvent(id1, namei余数, age18)
handle event3 - PersonSaveEvent(id1, namei余数, age18)四、总结
在Spring框架中EventListener注解用于标识监听器方法可以监听Spring ApplicationEvent或其他任意对象的事件。
当一个方法被标注为EventListener时它就被视为一个事件监听器可以接收和响应特定类型的事件。这个方法可以在事件发布时被调用以处理该事件。在Spring框架中EventListenerMethodProcessor负责寻找和调用带有EventListener注解的方法。
EventListener注解可以设置两个属性value和classes用于指定监听器监听的事件类型。如果value和classes没有设置值那么被标注的方法必须有一个且只能有一个参数。如果value或classes设置了值单个Class值表示监听器可以不用设置参数多个Class值则要求被标注的方法一定不要设置参数。这是因为在监听事件时通过反射监听方法处理事件事件转换成方法参数时若类型不一致可能会发生类型转换异常。
此外还有一个注解TransactionalEventListener它继承了EventListener的功能并添加了新的特性。这个注解可以用于在事务处理期间监听事件并且可以根据需要自动回滚事务。
总的来说EventListener是一种非常有用的技术可以帮助我们更好地管理和响应应用程序中的事件。在Spring框架中我们可以使用EventListener注解来方便地创建和注册事件监听器从而实现更高效的事件处理和控制。