如何扩展 Jersey 的参数注释?
How do I extend Jersey's param annotations?
据我所知,Jersey不支持deep-object参数(?type[n1]=v1&type[n2]=v2
形式的参数)。
是否可以将其添加为扩展名?如果是,怎么做?
我的想法是有一个类似于 @QueryParam
的注释,比方说 @DeepObjectParam
,我会用它来注释这样的字段:
@GET
public Response(@DeepObjectParam("type") Map<String, String> type) {
// ...
}
并让 Jersey 注入地图。
根据您使用的Jersey 版本,您需要实现的接口会有所不同。在 Jersey 2.0-2.25.1 中,class 是 ValueFactoryProvider
而 2.26+ 是 ValueParamProvider
.
对于两个 classes,实现将是相似的。有一种实现方法需要一个 Parameter
参数。我们使用此 Parameter
来检查此提供程序是否能够处理此类参数。如果检查通过,则该方法应该 return 提供实际参数的 Factory
或 Function
(取决于版本)。如果检查失败,它应该 return null.
比如参数注解为@DeepObjectParam
,参数类型为Map
,那么我们应该检查这两个东西。
@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
在这里,DeepParamFunction
是一个 Function
,它接受一个 ContainerRequest
参数。它将解析查询参数,然后 return Map
.
实现所需的 class 后,您需要将其注册到 Jersey。同样,根据您使用的 Jersey 版本,注册会有所不同(但相似)。在这两种情况下,您都需要使用 ResourceConfig
注册一个 AbstractBinder
register(new AbstractBinder() {
@Override
protected void configure() {
bind(DeepObjectParamProvider.class)
// 2.0-2.25.1 you will use ValueFactoryProvider.class
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
对于 Jersey 的两个版本,您将使用相同的 AbstractBinder
class,但导入会有所不同。在 2.0-2.25.1 中,您将在包名称中查找 hk2
。在 2.26 中,您将在包名称中查找 jersey
。另一个区别在于 to()
方法。在 2.0-2.25.1 中你将使用 ValueFactoryProvider
和 2.26+,你将使用 ValueParamProvider
.
这是 ValueParamProvider
的示例实现(对于 Jersey 2.26+)。 ValueFactoryProvider
的实现与
非常相似
public class DeepObjectParamProvider implements ValueParamProvider {
@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
private static boolean isStringStringMap(Parameter param) {
if (!param.getRawType().equals(Map.class)) {
return false;
}
ParameterizedType type = (ParameterizedType) param.getType();
Type[] genericTypes = type.getActualTypeArguments();
return genericTypes[0].equals(String.class) && genericTypes[1].equals(String.class);
}
@Override
public PriorityType getPriority() {
// Use HIGH otherwise it might not be used
return Priority.HIGH;
}
private static class DeepParamFunction implements Function<ContainerRequest, Map<String, String>> {
private final Parameter param;
private DeepParamFunction(Parameter param) {
this.param = param;
}
@Override
public Map<String, String> apply(ContainerRequest request) {
Map<String, String> map = new HashMap<>();
DeepObjectParam anno = param.getAnnotation(DeepObjectParam.class);
String paramName = anno.value();
MultivaluedMap<String, String> params = request.getUriInfo().getQueryParameters();
params.forEach((key, list) -> {
// do parsing of params
});
return map;
}
}
}
有关完整的 运行 (2.26+) 示例,请查看 this post. For versions earlier than 2.26, I've refactored that example and posted it to this Gist。
P.S.
在实现提供者和调试时,当多次调用该方法时不要感到惊讶。发生的事情是,在启动时,Jersey 将验证所有资源方法并确保能够处理所有参数。 Jersey 如何做到这一点是通过将每个参数传递给 所有 提供者,直到到达一个不 return 为空的提供者。所以你拥有的资源方法越多,你的提供者被调用的次数就越多。有关更多详细信息,请参阅 。
据我所知,Jersey不支持deep-object参数(?type[n1]=v1&type[n2]=v2
形式的参数)。
是否可以将其添加为扩展名?如果是,怎么做?
我的想法是有一个类似于 @QueryParam
的注释,比方说 @DeepObjectParam
,我会用它来注释这样的字段:
@GET
public Response(@DeepObjectParam("type") Map<String, String> type) {
// ...
}
并让 Jersey 注入地图。
根据您使用的Jersey 版本,您需要实现的接口会有所不同。在 Jersey 2.0-2.25.1 中,class 是 ValueFactoryProvider
而 2.26+ 是 ValueParamProvider
.
对于两个 classes,实现将是相似的。有一种实现方法需要一个 Parameter
参数。我们使用此 Parameter
来检查此提供程序是否能够处理此类参数。如果检查通过,则该方法应该 return 提供实际参数的 Factory
或 Function
(取决于版本)。如果检查失败,它应该 return null.
比如参数注解为@DeepObjectParam
,参数类型为Map
,那么我们应该检查这两个东西。
@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
在这里,DeepParamFunction
是一个 Function
,它接受一个 ContainerRequest
参数。它将解析查询参数,然后 return Map
.
实现所需的 class 后,您需要将其注册到 Jersey。同样,根据您使用的 Jersey 版本,注册会有所不同(但相似)。在这两种情况下,您都需要使用 ResourceConfig
AbstractBinder
register(new AbstractBinder() {
@Override
protected void configure() {
bind(DeepObjectParamProvider.class)
// 2.0-2.25.1 you will use ValueFactoryProvider.class
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
对于 Jersey 的两个版本,您将使用相同的 AbstractBinder
class,但导入会有所不同。在 2.0-2.25.1 中,您将在包名称中查找 hk2
。在 2.26 中,您将在包名称中查找 jersey
。另一个区别在于 to()
方法。在 2.0-2.25.1 中你将使用 ValueFactoryProvider
和 2.26+,你将使用 ValueParamProvider
.
这是 ValueParamProvider
的示例实现(对于 Jersey 2.26+)。 ValueFactoryProvider
的实现与
public class DeepObjectParamProvider implements ValueParamProvider {
@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter param) {
if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
return new DeepParamFunction(param);
}
return null;
}
private static boolean isStringStringMap(Parameter param) {
if (!param.getRawType().equals(Map.class)) {
return false;
}
ParameterizedType type = (ParameterizedType) param.getType();
Type[] genericTypes = type.getActualTypeArguments();
return genericTypes[0].equals(String.class) && genericTypes[1].equals(String.class);
}
@Override
public PriorityType getPriority() {
// Use HIGH otherwise it might not be used
return Priority.HIGH;
}
private static class DeepParamFunction implements Function<ContainerRequest, Map<String, String>> {
private final Parameter param;
private DeepParamFunction(Parameter param) {
this.param = param;
}
@Override
public Map<String, String> apply(ContainerRequest request) {
Map<String, String> map = new HashMap<>();
DeepObjectParam anno = param.getAnnotation(DeepObjectParam.class);
String paramName = anno.value();
MultivaluedMap<String, String> params = request.getUriInfo().getQueryParameters();
params.forEach((key, list) -> {
// do parsing of params
});
return map;
}
}
}
有关完整的 运行 (2.26+) 示例,请查看 this post. For versions earlier than 2.26, I've refactored that example and posted it to this Gist。
P.S.
在实现提供者和调试时,当多次调用该方法时不要感到惊讶。发生的事情是,在启动时,Jersey 将验证所有资源方法并确保能够处理所有参数。 Jersey 如何做到这一点是通过将每个参数传递给 所有 提供者,直到到达一个不 return 为空的提供者。所以你拥有的资源方法越多,你的提供者被调用的次数就越多。有关更多详细信息,请参阅