如何扩展Jersey的参数注释? [英] How do I extend Jersey's param annotations?

查看:117
本文介绍了如何扩展Jersey的参数注释?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知,Jersey不支持深层对象参数(?type [n1] = v1& type [n2] = v2 形式的参数).

是否可以将其添加为扩展名?如果可以,怎么办?

我的想法是要有一个类似于 @QueryParam 的注释,比如说 @DeepObjectParam ,我将用它来注释这样的字段:

 <代码> @GETpublic Response(@DeepObjectParam("type")Map< String,String>类型){//...} 

让Jersey注入地图.

解决方案

根据您使用的Jersey版本,您需要实现的接口会有所不同.在Jersey 2.0-2.25.1中,该类为 ValueFactoryProvider ,而在2.26+中,该类为 ValueParamProvider .

对于这两个类,实现将是相似的.有一种方法可以实现,该方法采用 Parameter 参数.我们使用此 Parameter 来检查此提供程序是否能够处理这种类型的参数.如果检查通过,则该方法应返回提供实际参数的 Factory Function (取决于版本).如果检查失败,则应返回null.

例如,如果参数用 @DeepObjectParam 注释,并且参数类型是 Map ,则我们应该检查这两件事.

  @Overridepublic Function< ContainerRequest,>getValueProvider(Parameter param){if(param.isAnnotationPresent(DeepObjectParam.class)&& isStringStringMap(param)){返回新的DeepParamFunction(param);}返回null;} 

在这里, DeepParamFunction 是一个采用单个 ContainerRequest 参数的 Function .它将执行查询参数的解析,然后返回 Map .

实现所需的类后,需要在Jersey进行注册.同样,根据您使用的Jersey版本,注册会有所不同(但类似).在这两种情况下,您都需要向 ResourceConfig

注册一个 AbstractBinder

  register(new AbstractBinder(){@Override受保护的无效configure(){绑定(DeepObjectParamProvider.class)//2.0-2.25.1,您将使用ValueFactoryProvider.class.to(ValueParamProvider.class).in(Singleton.class);}}); 

对于两个版本的Jersey,您将使用相同的 AbstractBinder 类,但是导入将有所不同.在2.0-2.25.1中,您将在程序包名称中查找 hk2 .在2.26中,您将在包名称中查找 jersey .另一个区别在于 to()方法.在2.0-2.25.1中,您将使用 ValueFactoryProvider 和2.26+,您将使用 ValueParamProvider .

这是 ValueParamProvider 的示例实现(适用于Jersey 2.26+). ValueFactoryProvider 的实现将非常相似

 公共类DeepObjectParamProvider实现ValueParamProvider {@Overridepublic Function< ContainerRequest,>getValueProvider(Parameter param){if(param.isAnnotationPresent(DeepObjectParam.class)&& isStringStringMap(param)){返回新的DeepParamFunction(param);}返回null;}私有静态布尔isStringStringMap(Parameter param){如果(!param.getRawType().equals(Map.class)){返回false;}ParameterizedType类型=(ParameterizedType)param.getType();Type [] genericTypes = type.getActualTypeArguments();返回genericTypes [0] .equals(String.class)&&genericTypes [1] .equals(String.class);}@Overridepublic PriorityType getPriority(){//使用HIGH,否则可能不会使用返回Priority.HIGH;}私有静态类DeepParamFunction实现Function< ContainerRequest,Map< String,String>>.{私有最终参数参数;私人DeepParamFunction(Parameter param){this.param = param;}@Override公共Map< String,String>apply(ContainerRequest请求){Map< String,String>map = new HashMap<>();DeepObjectParam anno = param.getAnnotation(DeepObjectParam.class);字符串paramName = anno.value();MultivaluedMap< String,String>params = request.getUriInfo().getQueryParameters();params.forEach((key,list)-> {//解析参数});返回地图;}}} 

有关完整运行示例(2.26及更高版本)的信息,请查看这篇文章.对于2.26之前的版本,我已经重构了该示例并将其发布到此要点./p>

P.S.

在实现提供程序和调试时,多次调用该方法不要感到惊讶.发生的情况是,启动时,Jersey将验证所有资源方法,并确保能够处理所有参数.泽西岛的工作方式是将每个参数传递给所有提供程序 all ,直到到达不返回null的提供程序为止.因此,您拥有的资源方法越多,您的提供者将被调用的次数就越多.有关详细说明,请参见这篇文章.

As far as I can see, Jersey does not support deep-object parameters (parameters in the form of ?type[n1]=v1&type[n2]=v2).

Is it possible to add this as extension? And if so, how?

My idea is to have an annotation similar to @QueryParam, let's say @DeepObjectParam, and I would use it to annotate a field like this:

   @GET
   public Response(@DeepObjectParam("type") Map<String, String> type) {
       // ...
   }

And have Jersey inject the map.

解决方案

Depending on the version of Jersey you are using, the interface that you need to implement will be different. In Jersey 2.0-2.25.1, the class is ValueFactoryProvider and 2.26+ it's ValueParamProvider.

With both classes, the implementation will be similar. There is one method to implement which takes a Parameter argument. We use this Parameter to check whether this provider is able to handle this type of parameter. If the check passes, then the method should return either a Factory or a Function (depending on the version) that provides the actual argument. If the check fails, it should return null.

For example, if the parameter is annotated with @DeepObjectParam and the parameter type is Map, then we should check for these two things.

@Override
public Function<ContainerRequest, ?> getValueProvider(Parameter param) {

    if (param.isAnnotationPresent(DeepObjectParam.class) && isStringStringMap(param)) {
        return new DeepParamFunction(param);
    }
    return null;
}

Here, the DeepParamFunction is a Function that takes a single ContainerRequest argument. It will do the parsing of the query parameter and then return the Map.

After you've implemented the required class, you need to register it with Jersey. Again, depending on which version of Jersey you are using, the registration will be different (but similar). In both cases you need to register an AbstractBinder with the ResourceConfig

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);
    }
});

For both versions of Jersey, you will use the same AbstractBinder class, but the imports will be different. In 2.0-2.25.1, you will look for hk2 in the package name. In 2.26, you will look for jersey in the package name. The other difference is in the to() method. In 2.0-2.25.1 you will use ValueFactoryProvider and 2.26+, you will use ValueParamProvider.

Here is an example implementation of the ValueParamProvider (for Jersey 2.26+). The implementation for ValueFactoryProvider will be very similar

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;
        }
    }
}

For a complete running (2.26+) example, take a look at this post. For versions earlier than 2.26, I've refactored that example and posted it to this Gist.

P.S.

While implementing the provider and debugging, don't be surprised when the method is called more than once. What happens is that on startup, Jersey will validate all the resource methods and make sure that all the parameters are able to be processed. How Jersey does this is by passing each Parameter to all the providers until one is reached that doesn't return null. So the more resource methods you have, the more times your provider will be called. See this post for more elaboration.

这篇关于如何扩展Jersey的参数注释?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆