如何创建一个<Integer>处理基元int的ParamConverter?



所以我想在jax-rs:中创建一个自定义ParamConverter<Integer>

public class IntParamConverter implements ParamConverter<Integer>{
    @Override
    public Integer fromString(String value) {
        try{
            return Integer.valueOf(value);
        }catch (Exception e){
            //do some stuff
            throw new BadRequestEx("Convert Exception");
        }
    }
    @Override
    public String toString(Integer value) {
        return String.valueOf(value);
    }
}

然后我通过ParamConverterProvider:注册

@Provider
public class CustomParamConverterHandler implements ParamConverterProvider {
        @Override
        public <T> ParamConverter<T> getConverter(final Class<T> rawType, Type genericType, Annotation[] annotations) {
            if (rawType.equals(Integer.class)){
                return (ParamConverter<T>) new IntParamConverter();
            }
            return null;
        }
    }

然后在资源POJO中使用它,如下所示:

    @GET
    @Path("/conv")
    @Produces({MediaType.TEXT_PLAIN})
    public String conv(@QueryParam("no") int no) {
        return ""+no;
    }

在运行时(使用system.out进行测试),我发现Tomee(Apache cfx?)没有使用我的IntParamConverter。如果我声明@QueryParam("no") Integer no而不是@QueryParam("no") int no,那么一切都很好(tomee使用我的自定义paramConverter.

我尝试用基元int:注册我的IntParamConverter

if (rawType.equals(int.class)){
     return (ParamConverter<T>) new IntParamConverter();
}

但随后出现运行时异常:

java.lang.ClassCastException: Cannot cast java.lang.Integer to int
    at java.lang.Class.cast(Class.java:3369)
    at org.apache.cxf.jaxrs.utils.InjectionUtils.handleParameter(InjectionUtils.java:388)
    at org.apache.cxf.jaxrs.utils.InjectionUtils.createParameterObject(InjectionUtils.java:978)
    at org.apache.cxf.jaxrs.utils.JAXRSUtils.readQueryString(JAXRSUtils.java:1210)

如何创建这样一个处理基元int的ParamConverter?

你不能。

Java泛型在编译时都转换为java.lang.Object(通过一个称为类型擦除的过程)。

诸如int之类的基元类型没有java.lang.Object作为它们的基类,因此在这种情况下这种转换是不可能的。

我不会明确地说这是一个bug,但他们对InjectionUtils.handleParameter的实现很奇怪。他们有以下

// [...]
Object result = null;
try {
    result = createFromParameterHandler(value, pClass, genericType, paramAnns, message); 
} catch (IllegalArgumentException nfe) {
    throw createParamConversionException(pType, nfe);
}

从您的ParamConveter中获取值。然后他们做这个

if (result != null) {
    return pClass.cast(result);
}
// [...]

显然,cast将被尝试并失败。但是没有理由对基元类型进行这样的强制转换,反射的Method#invoke会处理这个问题。换句话说,您可以(而且必须)将Integer值传递给Method#invoke,运行时将负责为您转换它。

如果他们想进行额外的验证,他们可以首先检查目标类型是否是基元类型,然后进行适当的验证(检查您的值是否属于相应的包装类型)。对于其他类型,它们可以回退到cast(因为方法返回类型是绑定到pClass的通用T)或使用类似isInstance的东西。

我不会向开发人员开罚单,但一个使用率如此之高的库应该能够做到这一点。


如何创建这样一个处理基元int的ParamConverter?

你不能。ParamConverter是一个泛型类,基元不能与泛型一起使用。然而,库应该支持为您进行最后的Integerint转换,但似乎没有。

相关内容

最新更新