所以我想在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
是一个泛型类,基元不能与泛型一起使用。然而,库应该支持为您进行最后的Integer
到int
转换,但似乎没有。