Spring泛型ApplicationEvent监听器拦截问题及多场景实现方案问询
问题解答
现象是否符合预期?
符合Spring默认行为预期。
原因是Java的泛型存在运行时擦除机制,你声明的方法参数BaseEvent<String>在运行时会被擦除为原始类型BaseEvent。而Spring默认的@EventListener在没有额外配置的情况下,仅会根据擦除后的原始类型匹配事件,所以所有BaseEvent及其子类事件都会被该监听器拦截,和你观察到的现象一致。
三类需求的实现方案
如果需要实现泛型精准匹配,首先建议做前置配置:修改BaseEvent类实现ResolvableTypeProvider接口,向Spring暴露泛型类型信息,配置后Spring就可以正确识别泛型参数完成匹配:
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableTypeProvider; import org.springframework.context.ApplicationEvent; public class BaseEvent<T> extends ApplicationEvent implements ResolvableTypeProvider { protected T value; public BaseEvent(Object source, T value) { super(source); this.value = value; } @Override public ResolvableType getResolvableType() { // 暴露当前事件的实际泛型类型 return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(value)); } }
1. 拦截所有泛型类型的BaseEvent事件
直接将监听器参数声明为无边界通配符的BaseEvent即可:
@EventListener public void onAllBaseEvent(BaseEvent<?> event) { // 处理逻辑 }
该监听器会匹配所有BaseEvent及其子类的事件,不管泛型是什么类型。
2. 只拦截指定泛型类型的BaseEvent事件
完成上述前置的ResolvableTypeProvider配置后,直接在方法参数里指定你要拦截的泛型类型即可,比如只拦截泛型为String的BaseEvent:
@EventListener public void onStringBaseEvent(BaseEvent<String> event) { // 只会收到泛型类型为String的BaseEvent事件 }
如果你不想修改BaseEvent类,也可以用SpEL条件判断实现:
// 不需要修改BaseEvent类,通过SpEL运行时判断value类型 @EventListener(condition = "#event.value != null and #event.value instanceof T(java.lang.String)") public void onStringBaseEvent(BaseEvent event) { // 处理逻辑 }
注意:使用SpEL条件判断时补充非空校验可避免value为null时触发空指针异常。
3. 拦截所有BaseStringEvent事件(含对应的BaseEvent事件)
在完成ResolvableTypeProvider配置的前提下,直接用BaseEvent<String>作为参数即可满足需求:
@EventListener public void onStringEvent(BaseEvent<String> event) { // 同时匹配直接发布的BaseEvent<String>事件,以及子类BaseStringEvent的所有事件 }
因为BaseStringEvent是BaseEvent<String>的子类,Spring的事件匹配会自动包含父类参数对应的子类实例,无需额外配置。
内容的提问来源于stack exchange,提问作者Igor Chestnov
相关产品推荐
相关产品推荐

