Java自定义注解能否接收Function类型参数?求实现示例
问题解答
Java注解不能直接接收java.util.function.Function类型的参数。Java规范明确限定了注解的参数类型只能是以下几种:
- 基本数据类型(int、boolean等)
StringClass- 枚举类型
- 注解类型
- 以上类型的一维数组
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
相关产品推荐
相关产品推荐

