我很难用一种干净利落的方式来实现这个 JsonParamInjectable。我在这个论坛和其他地方进行了搜索,但没有发现任何提示可以告诉我如何干净利落地实现它。
对于 jaxrs 方法:
public Object add(
@JsonParam("a") int a,
@JsonParam("b") int b
)
它将 json {"a":1, "b":2} 解析为参数 a 和 b
为此,我实现了一个 InjectableProvider,它通过方法参数创建一个 JsonInjectable 实例。
@Provider
public class JsonParamProvider implements InjectableProvider<JsonParam, Type> {
private Gson gson;
public JsonParamProvider(@Context ServletConfig sc) throws Exception {
super();
this.gson = GsonFactory.newGson(sc);
}
@Override
public Injectable<Object> getInjectable(ComponentContext cc, JsonParam a, Type type) {
if (a.value() != null) {
String signature = cc.getAccesibleObject().toString();
return new JsonInjectable(signature, a, type, gson);
}
return null;
}
@Override
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
魔术是在这个 JsonInjectable 中完成的,它的 where id 做了一个肮脏的把戏:
public class JsonInjectable extends AbstractHttpContextInjectable<Object> {
private final JsonParam param;
private final Type type;
private final String signature;
private final Gson gson;
private static ThreadLocal<WeakReference<HttpContext>> contextCache = new ThreadLocal<WeakReference<HttpContext>>(){
@Override
protected WeakReference<HttpContext> initialValue() {
return new WeakReference<HttpContext>(null);
}
};
private static ThreadLocal<WeakReference<JsonElement>> entityCache = new ThreadLocal<WeakReference<JsonElement>>(){
@Override
protected WeakReference<JsonElement> initialValue() {
return new WeakReference<JsonElement>(null);
}
};
public JsonInjectable(String signature, JsonParam param, Type type, Gson gson) {
this.signature = signature;
this.param = param;
this.type = type;
this.gson = gson;
}
@Override
public Object getValue(HttpContext context) {
try {
JsonElement methodJsonElement = entityCache.get().get();
HttpContext context2 = contextCache.get().get();
if (context != context2) {
contextCache.set(new WeakReference<HttpContext>(context));
String entity = context.getRequest().getEntity(String.class);
System.out.println("entity:"+entity);
JsonParser parser = new JsonParser();
methodJsonElement = parser.parse(entity);
entityCache.set(new WeakReference<JsonElement>(methodJsonElement));
}
if (methodJsonElement == null || methodJsonElement.isJsonNull()) {
return null;
}
final JsonElement valueJsonElement = ((JsonObject)methodJsonElement).get(this.param.value());
if (valueJsonElement == null || valueJsonElement.isJsonNull()) {
return null;
}
if (this.type.equals(java.lang.Integer.class)) {
Number number = valueJsonElement.getAsNumber();
return number.intValue();
}
if (this.type.equals(java.lang.String.class)) {
return valueJsonElement.getAsString();
}
Class<?> c = ((Class<?>) this.type);
if (int.class.equals(c)) {
return valueJsonElement.getAsInt();
}
//other parsing code...
//try with gson
return this.gson.fromJson(valueJsonElement, this.type);
} catch (RuntimeException e) {
throw e;
}
}
问题是,在某些情况下,实体是空的,我怀疑是一个有效的 http 请求。导致 java.io.EOFException: End of input at line 1 column 2. 这个问题出现在生产中,但我无法在测试环境中重现它。
如果有问题,肯定与“context != context2”有关。对于每个可注入对象都绑定到一个参数,并且以我无法控制的顺序调用可注入对象,并且每个可注入对象都处理相同的数据:从请求实体解析 json。所以为了避免每次都重新解析实体,我使用 context != context2 来检测它是否是一个新请求。
检测新请求的好方法是什么,因此每个请求只能发生 1 次 json 解析。