Quarkus拦截器报错‘Interceptor has no bindings’及属性变更事件实现咨询
解决Quarkus拦截器绑定错误及属性变更事件优化方案
一、修复「Interceptor has no bindings」错误
这个错误的核心是拦截器缺少绑定注解关联,按以下步骤修正:
- 创建正确的拦截器绑定注解
import jakarta.interceptor.InterceptorBinding; import java.lang.annotation.*; @InterceptorBinding @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NotifyOnChange { }
注意使用Jakarta EE包(Quarkus已从javax迁移到jakarta),注解需标注@InterceptorBinding才能作为拦截器的绑定标识。
- 修改拦截器类,关联绑定注解
import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.inject.Inject; @NotifyOnChange // 绑定自定义注解 @Interceptor // 必须标注此注解声明为拦截器 public class DMNChangedInterceptor { @Inject EventBus eventBus; @AroundInvoke public Object intercept(InvocationContext ctx) throws Exception { String methodName = ctx.getMethod().getName(); if (methodName.startsWith("set")) { // 解析属性名、获取新旧值 String fieldName = methodName.substring(3,4).toLowerCase() + methodName.substring(4); var field = ctx.getTarget().getClass().getDeclaredField(fieldName); field.setAccessible(true); Object oldValue = field.get(ctx.getTarget()); // 执行原setter方法 Object result = ctx.proceed(); // 对比值并发送事件 Object newValue = field.get(ctx.getTarget()); if (oldValue == null ? newValue != null : !oldValue.equals(newValue)) { eventBus.publish("dmn.changed", new DMNChangeEvent(fieldName, oldValue, newValue)); } return result; } return ctx.proceed(); } }
- 在目标类的setter或类上标注绑定注解
public class DMNEntity { private String name; private int version; @NotifyOnChange public void setName(String name) { this.name = name; } @NotifyOnChange public void setVersion(int version) { this.version = version; } }
关于beans.xml的说明
Quarkus默认使用annotated模式的Bean发现,只要拦截器和绑定注解都正确标注了CDI相关注解,不需要额外配置beans.xml。如果项目中存在旧的beans.xml,确保其bean-discovery-mode不是none即可。
二、更高效的属性变更事件实现方案
拦截器依赖反射,运行时存在一定性能开销,可根据场景选择以下替代方案:
1. JPA实体场景:使用生命周期监听器
如果目标类是JPA实体,直接利用JPA生命周期注解(如@PreUpdate)或Quarkus Panache的实体监听器,无需拦截setter:
import io.quarkus.hibernate.orm.panache.PanacheEntity; import jakarta.persistence.PreUpdate; import jakarta.inject.Inject; import io.vertx.mutiny.core.eventbus.EventBus; public class DMNEntityListener { @Inject EventBus eventBus; @PreUpdate public void onEntityUpdate(DMNEntity entity) { // 可结合JPA的DirtyChecking逻辑对比属性变更,再发送事件 eventBus.publish("dmn.changed", entity); } }
在实体类上标注监听器:
@EntityListeners(DMNEntityListener.class) public class DMNEntity extends PanacheEntity { // 实体字段... }
2. 响应式场景:使用Mutiny Multi实现属性流
适合Quarkus响应式架构,无需反射,性能更优:
import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.subjects.MultiSubject; import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.inject.Inject; public class DMNEntity { private String name; private final MultiSubject<String> nameChangeStream = MultiSubject.create(); @Inject EventBus eventBus; public DMNEntity() { // 订阅属性变更流,自动发送事件 nameChangeStream.subscribe().with(newName -> eventBus.publish("dmn.name.changed", newName) ); } public void setName(String name) { this.name = name; nameChangeStream.emit(name); } // 对外暴露变更流,供其他组件订阅 public Multi<String> getNameChanges() { return nameChangeStream; } }
3. 编译时生成代码:结合Lombok扩展
自定义Lombok注解处理器,在编译阶段自动生成带事件发送逻辑的setter,完全避免运行时反射开销。例如自定义@NotifyOnChange注解,配合Lombok的@Setter扩展,在生成setter代码时插入事件发送片段。
内容的提问来源于stack exchange,提问作者John Boudreaux
相关产品推荐
相关产品推荐

