#####0.接手新项目 最近接手一个项目,大部分功能都已经完成的半残项目.10+个工程相互调用,根本没有文档,就连注释都少的可怜。是不是有点蛋疼呢,或许每个换新项目的人都是这个感受吧。
#####1.接口没检查参数 测试一个接口的时候,没有传入参数,居然返回我一堆java异常,StackOverflowError…
type Exception report
message org.apache.cxf.interceptor.Fault
description The server encountered an internal error (org.apache.cxf.interceptor.Fault) that prevented it from fulfilling this request.
exception
java.lang.RuntimeException: org.apache.cxf.interceptor.Fault
...
root cause
org.apache.cxf.interceptor.Fault
...
root cause
java.lang.StackOverflowError
java.lang.System.arraycopy(Native Method)
java.lang.String.getChars(String.java:854)
java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:391)
java.lang.StringBuffer.append(StringBuffer.java:224)
java.io.StringWriter.write(StringWriter.java:84)
com.google.gson.stream.JsonWriter.string(JsonWriter.java:534)
com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:402)
com.google.gson.stream.JsonWriter.nullValue(JsonWriter.java:431)
com.google.gson.stream.JsonWriter.value(JsonWriter.java:415)
com.google.gson.internal.bind.TypeAdapters$13.write(TypeAdapters.java:362)
com.google.gson.internal.bind.TypeAdapters$13.write(TypeAdapters.java:346)
com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
#####2.捕获异常 项目用cxf做的 WebService, 在这里catch到.
cxf-rt-core:AbstractInvoker.java
protected Object invoke(Exchange exchange, final Object serviceObject, Method m, List<Object> params) {
Object res;
try {
Object[] paramArray = new Object[]{};
if (params != null) {
paramArray = params.toArray();
}
res = performInvocation(exchange, serviceObject, m, paramArray);
if (exchange.isOneWay()) {
return null;
}
return new MessageContentsList(res);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t == null) {
t = e;
}
// ...
}
#####3.异常点 使用google的gson将数据转换为json格式.在 toJson 的时候 抽象类TypeAdapter使用了TypeAdapterRuntimeTypeWrapper的实现
Gson.java
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
在TypeAdapterRuntimeTypeWrapper中一直在循环调用自己类的write方法.
package com.google.gson.internal.bind;
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T>
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter out, T value) throws IOException {
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
TypeAdapter chosen = delegate;
Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) {
TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter;
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type
chosen = delegate;
} else {
// Use the type adapter for runtime type
chosen = runtimeTypeAdapter;
}
}
chosen.write(out, value);
}
#####4.测试 不就是这么个对象么,为什么会使gson陷入循环呢?难道是gson的Bug?自己构建个对象来toJson试试.
RespObj obj = new RespObj();
obj.setCode("10199");
obj.setType("PARAMETEREXCEPTION");
obj.setMessage("java.lang.NullPointerException");
new Gson().toJson(obj);
自己构造了这个对象,调用gson并没有异常呢。
#####5.原因在哪?
对比了下,原来项目中,message并不是字符串”java.lang.NullPointerException”, 而是 java.lang.NullPointerException 对象…
这么做就会看到给异常了:
new Gson().toJson(new NullPointerException());
#####6.关于异常处理结论
-
原来所有的异常继承自Exception,Exception继承 Throwable,Throwable有个this指针.导致toJson的时候产生了循环.好吧,Gson并没有这个bug,是开发人员自己作死
-
对用户输入进行检测
-
最好是不要直接把异常抛给用户,大部分用户是看不懂的
-
内部异常往外抛,在最外层捕获,以友好的方式显示给用户
-
在最外层统一处理,尽量避免在每个servlet去处理
-
而且配置404,500返回页面,不要直接将异常打印