Spring的Event事件处理使用详解

37 篇文章 5 订阅

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。

转载请标明出处,欢迎扫码加关注。
扫一扫关注微信公众号

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值