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

Java自定义注解能否接收Function类型参数?求实现示例

问题解答

Java注解不能直接接收java.util.function.Function类型的参数。Java规范明确限定了注解的参数类型只能是以下几种:

  • 基本数据类型(int、boolean等)
  • String
  • Class
  • 枚举类型
  • 注解类型
  • 以上类型的一维数组

Function属于函数式接口,不在允许的参数类型范围内,但我们可以通过传入Function实现类的Class对象间接实现需求,后续在注解的处理逻辑中通过反射实例化该类并调用方法。

实现步骤

1. 修改@InBound注解定义

新增一个Class类型的参数,要求该类必须实现Function<ContainerRequestContext, String>(适配Jersey环境,以请求上下文为输入,输出唯一标识字符串),同时提供一个默认实现类:

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface InBound {
    boolean requestLog() default false;
    boolean responseLog() default false;
    // 新增参数:指定提取唯一标识的Function实现类
    Class<? extends Function<ContainerRequestContext, String>> idExtractor() default DefaultIdExtractor.class;
}

2. 定义默认标识提取器

创建默认实现类,示例为提取请求头X-Request-ID,不存在则生成UUID作为兜底:

public class DefaultIdExtractor implements Function<ContainerRequestContext, String> {
    @Override
    public String apply(ContainerRequestContext requestContext) {
        String requestId = requestContext.getHeaderString("X-Request-ID");
        return requestId != null ? requestId : UUID.randomUUID().toString();
    }
}

3. 在Jersey拦截器中处理注解逻辑

修改日志拦截器(实现ContainerRequestFilter和ContainerResponseFilter),实例化指定的提取器并提取唯一标识:

@InBound
@Provider
public class InBoundLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        InBound annotation = getInBoundAnnotation(requestContext);
        if (annotation != null && annotation.requestLog()) {
            try {
                // 实例化标识提取器
                Function<ContainerRequestContext, String> idExtractor = annotation.idExtractor().newInstance();
                String requestId = idExtractor.apply(requestContext);
                // 这里加入requestId关联的请求日志入库逻辑
                // ...
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException("实例化标识提取器失败", e);
            }
        }
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        InBound annotation = getInBoundAnnotation(requestContext);
        if (annotation != null && annotation.responseLog()) {
            // 复用requestId完成响应日志入库关联
            // ...
        }
    }

    // 辅助方法:获取当前请求对应的@InBound注解
    private InBound getInBoundAnnotation(ContainerRequestContext requestContext) {
        ResourceInfo resourceInfo = requestContext.getProperty(ResourceInfo.class.getName());
        if (resourceInfo != null) {
            InBound methodAnnotation = resourceInfo.getResourceMethod().getAnnotation(InBound.class);
            if (methodAnnotation != null) {
                return methodAnnotation;
            }
            return resourceInfo.getResourceClass().getAnnotation(InBound.class);
        }
        return null;
    }
}

4. 使用自定义标识提取器

如果需要从请求参数提取traceId,可以创建自定义实现类:

public class TraceIdExtractor implements Function<ContainerRequestContext, String> {
    @Override
    public String apply(ContainerRequestContext requestContext) {
        return requestContext.getUriInfo().getQueryParameters().getFirst("traceId");
    }
}

然后在资源方法上指定该提取器:

@Path("/api")
public class MyResource {

    @GET
    @Path("/test")
    @InBound(requestLog = true, responseLog = true, idExtractor = TraceIdExtractor.class)
    public Response test() {
        return Response.ok("success").build();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:50:25