You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus拦截器报错‘Interceptor has no bindings’及属性变更事件实现咨询

解决Quarkus拦截器绑定错误及属性变更事件优化方案

一、修复「Interceptor has no bindings」错误

这个错误的核心是拦截器缺少绑定注解关联,按以下步骤修正:

  1. 创建正确的拦截器绑定注解
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才能作为拦截器的绑定标识。

  1. 修改拦截器类,关联绑定注解
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();
    }
}
  1. 在目标类的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 14:35:18