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

Jakarta EE项目中如何向自定义注解拦截器传递运行时参数?

问题1解答

Java注解的参数必须是编译时常量,无法直接传递userContext.getUserId()这类运行时计算的值,你的尝试不符合Java语法规范,这个思路行不通。

问题2:Jakarta EE规范内的解决方案

以下几种方案均符合Jakarta EE规范,且能满足依赖隔离要求(依赖A无需访问依赖B的UserContext):

方案1:使用EL表达式传递运行时值

修改@MyLog注解,添加支持EL表达式的参数,通过Jakarta EE的EL解析器在拦截器中动态解析值:

  1. 更新@MyLog注解:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface MyLog {
    String userIdExpr() default "";
}
  1. 在业务类中使用EL表达式引用UserContext的属性:
@Named
@ViewScoped
public class MyClass {
    @Inject
    private UserContext userContext;
    
    @MyLog(userIdExpr = "#{userContext.userId}")
    public void myMethod() {
        // 方法逻辑
    }
}
  1. 拦截器中解析EL表达式:
@Interceptor
@MyLog
public class MyLogInterceptor {
    @Inject
    private jakarta.el.ELContext elContext;
    @Inject
    private jakarta.el.ExpressionFactory expressionFactory;

    @AroundInvoke
    public Object logMethodInvocation(InvocationContext ctx) throws Exception {
        MyLog myLog = ctx.getMethod().getAnnotation(MyLog.class);
        String userIdExpr = myLog.userIdExpr();
        String userId = null;

        if (!userIdExpr.isEmpty()) {
            ValueExpression valueExpr = expressionFactory.createValueExpression(
                elContext, userIdExpr, String.class);
            userId = (String) valueExpr.getValue(elContext);
        }

        // 执行你的日志逻辑,使用userId
        // ...

        return ctx.proceed();
    }
}

此方案完全隔离了依赖A和B,依赖A仅需处理EL表达式,无需知晓UserContext的存在。

方案2:通过反射从目标实例获取数据

拦截器可通过InvocationContext获取被调用的业务类实例,再通过反射提取UserContext中的用户ID:

@Interceptor
@MyLog
public class MyLogInterceptor {

    @AroundInvoke
    public Object logMethodInvocation(InvocationContext ctx) throws Exception {
        String userId = null;
        Object targetBean = ctx.getTarget();

        // 反射获取userContext字段
        Field userContextField = targetBean.getClass().getDeclaredField("userContext");
        userContextField.setAccessible(true);
        Object userContext = userContextField.get(targetBean);

        // 调用getUserId方法
        Method getUserIdMethod = userContext.getClass().getMethod("getUserId");
        userId = (String) getUserIdMethod.invoke(userContext);

        // 执行日志逻辑
        // ...

        return ctx.proceed();
    }
}

注意:需处理反射相关异常,若字段/方法名不固定,可通过注解参数指定名称提升灵活性。

方案3:利用CDI Instance动态查找UserContext

若UserContext是CDI托管bean,可通过Instance动态查找实例,避免直接依赖类:

@Interceptor
@MyLog
public class MyLogInterceptor {
    @Inject
    private Instance<Object> instance;

    @AroundInvoke
    public Object logMethodInvocation(InvocationContext ctx) throws Exception {
        String userId = null;
        try {
            // 通过类名字符串加载类,避免直接依赖
            Class<?> userContextClass = Class.forName("com.example.UserContext");
            Object userContext = instance.select(userContextClass).get();
            Method getUserIdMethod = userContextClass.getMethod("getUserId");
            userId = (String) getUserIdMethod.invoke(userContext);
        } catch (Exception e) {
            // 异常处理,比如跳过日志或抛出
            e.printStackTrace();
        }

        // 执行日志逻辑
        // ...

        return ctx.proceed();
    }
}

此方案通过类名的字符串形式绕开了直接依赖,同时利用CDI的依赖查找机制获取实例。


内容的提问来源于stack exchange,提问作者maxc123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 00:39:52