Spring中使用事件非常简单,只需要以下的几个步骤:
- 1.定义事件,继承ApplicationEvent
- 2.定义监听,要么实现ApplicationListener接口,要么在方法上添加@EventListener注解
- 3.发布事件,调用ApplicationContext.publishEvent()或者ApplicationEventPublisher.publishEvent();
下面我们就用一个例子来说下具体的用法:
比如用户注册成功以后,系统要给用户发送一封邮件,同时还要给用户发放优惠券,为了跟注册流程解耦,可以在注册成功以后发出一个事件,让其他服务来监听。
定义用户注册成功事件:
//自定义事件需要继承ApplicationEvent
public 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> {
@Override
public void onApplicationEvent(UserRegisterEvent event) {
System.out.println(Thread.currentThread().getName()+"-给用户"+event.getUsername()+"发送邮件!");
}
}
//也可以使用@EventListener监听事件
@Component
@Order(1)
public class CouponUserRegisterListener {
@EventListener
public void sendCoupon(UserRegisterEvent event) {
System.out.println(Thread.currentThread().getName()+"-给用户"+event.getUsername()+"发送优惠券!");
}
}
用户注册成功以后发送事件出来:
@Service
public 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;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public 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
@ComponentScan
public 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:
@Override
public 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
@ComponentScan
public class AppConfig {
@Bean //另一种异步的方式,这样即使不加Async也可以异步,注意bean的名字
public ApplicationEventMulticaster applicationEventMulticaster(){
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(this.taskExecutor().getObject());
return eventMulticaster;
}
/**
* 注意这个bean的名字,如果是taskExecutor,那么@Async也会使用这个线程池,否则@Async不会使用,原因请参考前一篇
* */
@Bean
public ThreadPoolExecutorFactoryBean taskExecutor(){
ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
result.setThreadNamePrefix("event-pool-");
result.setCorePoolSize(5);
return result;
}
}
完整的源码下载:https://github.com/xjs1919/enumdemo 下面的event-demo。
转载请标明出处,欢迎扫码加关注。
网友评论