Spring中使用事件非常简单,只需要以下的几个步骤:
1.定义事件,继承ApplicationEvent
2.定义监听,要么实现ApplicationListener接口,要么在方法上添加@EventListener注解
3.发布事件,调用ApplicationContext.publishEvent()或者ApplicationEventPublisher.publishEvent();
下面我们就用一个例子来说下具体的用法:
比如用户注册成功以后,系统要给用户发送一封邮件,同时还要给用户发放优惠券,为了跟注册流程解耦,可以在注册成功以后发出一个事件,让其他服务来监听。
定义用户注册成功事件:
//自定义事件需要继承ApplicationEventpublic class UserRegisterEvent extends ApplicationEvent {private String username;public UserRegisterEvent(Object source, String username) {super(source);this.username = username;}public String getUsername() {return username;}}
定义发送邮件和发放优惠券的监听:
//可以实现ApplicationListener接口监听事件@Component@Order(2)//可以使用order指定顺序,越小优先级越高public class MailUserRegisterListener implements ApplicationListener<UserRegisterEvent> {@Overridepublic void onApplicationEvent(UserRegisterEvent event) {System.out.println(Thread.currentThread().getName()+"-给用户"+event.getUsername()+"发送邮件!");}}//也可以使用@EventListener监听事件@Component@Order(1)public class CouponUserRegisterListener {@EventListenerpublic void sendCoupon(UserRegisterEvent event) {System.out.println(Thread.currentThread().getName()+"-给用户"+event.getUsername()+"发送优惠券!");}}
用户注册成功以后发送事件出来:
@Servicepublic class UserRegisterService implements ApplicationContextAware, ApplicationEventPublisherAware {private ApplicationContext applicationContext;private ApplicationEventPublisher applicationEventPublisher;public boolean userRegister(String username){System.out.println("用户"+username+"注册成功");//可以用applicationContext发送事件applicationContext.publishEvent(new UserRegisterEvent(this, username));//也可以用applicationEventPublisher,二者等价//applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));return true;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher){this.applicationEventPublisher = applicationEventPublisher;}}
运行下:
public class AsyncMain {public static void main(String[] args)throws Exception {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);UserRegisterService service = ctx.getBean(UserRegisterService.class);service.userRegister("xjs");}}
假如我们想把监听线程跟主线程异步,该如何做呢?根据上一篇文章,只需要启用@EnableAsync,然后在监听的方法上加上@Async即可。
@EnableAsync //启用异步@Configuration@ComponentScanpublic class AppConfig {}@Component@Order(3)public class AsyncUserRegisterListener implements ApplicationListener<UserRegisterEvent> {@Override@Async //注意这个注解,这样就可以在子线程中监听事件了public void onApplicationEvent(UserRegisterEvent event) {System.out.println(Thread.currentThread().getName()+"-收到用户注册事件,异步执行任务");}}
看下事件处理相关的源码处理:
在容器启动的时候AbstractApplicationContext#refresh()里面会执行:AbstractApplicationContext#initApplicationEventMulticaster();
protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}}else {this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}}
这里首先是从容器中去取名字是applicationEventMulticaster并且类型是ApplicationEventMulticaster的bean,如果取不到就创建一个SimpleApplicationEventMulticaster。实际上,Spring中仅有一个ApplicationEventMulticaster,那就是SimpleApplicationEventMulticaster。
发送事件的时候AbstractApplicationContext#publishEvent,最终还是交给SimpleApplicationEventMulticaster#multicastEvent:
@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}}
如果能获取到executor就异步执行,否则就同步执行。
因此,我们也可以通过给SimpleApplicationEventMulticaster设置executor来做异步处理,比如:
@EnableAsync@Configuration@ComponentScanpublic class AppConfig {@Bean //另一种异步的方式,这样即使不加Async也可以异步,注意bean的名字public ApplicationEventMulticaster applicationEventMulticaster(){SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.setTaskExecutor(this.taskExecutor().getObject());return eventMulticaster;}/*** 注意这个bean的名字,如果是taskExecutor,那么@Async也会使用这个线程池,否则@Async不会使用,原因请参考前一篇* */@Beanpublic ThreadPoolExecutorFactoryBean taskExecutor(){ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();result.setThreadNamePrefix("event-pool-");result.setCorePoolSize(5);return result;}}
完整的源码下载:https://github.com/xjs1919/enumdemo 下面的event-demo。




