bean转换为json时,使用了反射获取,性能较差。如果数据量稍大一点,转换时的性能是个问题。
希望后续版本加个以空间换性能的方法。
简单修改了一下系统的json转换的代码,测试了性能差距,真的差很多。代码和测试数据如下:
这是修改后的代码:
/**
* Json 转换 JFinal 实现.
*
* json 到 java 类型转换规则:
* string java.lang.String
* number java.lang.Number
* true|false java.lang.Boolean
* null null
* array java.util.List
* object java.util.Map
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class JFinalJson extends Json {
private final static Map<String,List<MethodInfo>> beanGetMethodCache = new HashMap<String,List<MethodInfo>>();
private static int defaultConvertDepth = 15;
private boolean useBeanCache = true; //是否使用缓存,如果设置为false,不使用缓存,每次均反射获取bean的get方法
protected int convertDepth = defaultConvertDepth;
protected String timestampPattern = "yyyy-MM-dd HH:mm:ss";
// protected String datePattern = "yyyy-MM-dd";
/**
* 设置全局性默认转换深度
*/
public static void setDefaultConvertDepth(int defaultConvertDepth) {
if (defaultConvertDepth < 2) {
throw new IllegalArgumentException("defaultConvertDepth depth can not less than 2.");
}
JFinalJson.defaultConvertDepth = defaultConvertDepth;
}
public JFinalJson setConvertDepth(int convertDepth) {
if (convertDepth < 2) {
throw new IllegalArgumentException("convert depth can not less than 2.");
}
this.convertDepth = convertDepth;
return this;
}
public JFinalJson setTimestampPattern(String timestampPattern) {
if (StringUtils.isBlank(timestampPattern)) {
throw new IllegalArgumentException("timestampPattern can not be blank.");
}
this.timestampPattern = timestampPattern;
return this;
}
public Json setDatePattern(String datePattern) {
if (StringUtils.isBlank(datePattern)) {
throw new IllegalArgumentException("datePattern can not be blank.");
}
this.datePattern = datePattern;
return this;
}
public static JFinalJson getJson() {
return new JFinalJson();
}
protected String mapToJson(Map map, int depth) {
if(map == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
boolean first = true;
Iterator iter = map.entrySet().iterator();
sb.append('{');
while(iter.hasNext()){
if(first)
first = false;
else
sb.append(',');
Map.Entry entry = (Map.Entry)iter.next();
toKeyValue(String.valueOf(entry.getKey()),entry.getValue(), sb, depth);
}
sb.append('}');
return sb.toString();
}
protected void toKeyValue(String key, Object value, StringBuilder sb, int depth){
sb.append('\"');
if(key == null)
sb.append("null");
else
escape(key, sb);
sb.append('\"').append(':');
sb.append(toJson(value, depth));
}
protected String iteratorToJson(Iterator iter, int depth) {
boolean first = true;
StringBuilder sb = new StringBuilder();
sb.append('[');
while(iter.hasNext()){
if(first)
first = false;
else
sb.append(',');
Object value = iter.next();
if(value == null){
sb.append("null");
continue;
}
sb.append(toJson(value, depth));
}
sb.append(']');
return sb.toString();
}
/**
* Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F).
*/
protected String escape(String s) {
if(s == null)
return null;
StringBuilder sb = new StringBuilder();
escape(s, sb);
return sb.toString();
}
protected void escape(String s, StringBuilder sb) {
for(int i=0; i<s.length(); i++){
char ch = s.charAt(i);
switch(ch){
case '"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
//case '/':
// sb.append("\\/");
// break;
default:
if((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) {
String str = Integer.toHexString(ch);
sb.append("\\u");
for(int k=0; k<4-str.length(); k++) {
sb.append('0');
}
sb.append(str.toUpperCase());
}
else{
sb.append(ch);
}
}
}
}
public String toJson(Object object) {
return toJson(object, convertDepth);
}
protected String toJson(Object value, int depth) {
if(value == null || (depth--) < 0)
return "null";
if(value instanceof String)
return "\"" + escape((String)value) + "\"";
if(value instanceof Double){
if(((Double)value).isInfinite() || ((Double)value).isNaN())
return "null";
else
return value.toString();
}
if(value instanceof Float){
if(((Float)value).isInfinite() || ((Float)value).isNaN())
return "null";
else
return value.toString();
}
if(value instanceof Number)
return value.toString();
if(value instanceof Boolean)
return value.toString();
if (value instanceof java.util.Date) {
if (value instanceof java.sql.Timestamp) {
return "\"" + new SimpleDateFormat(timestampPattern).format(value) + "\"";
}
if (value instanceof java.sql.Time) {
return "\"" + value.toString() + "\"";
}
// 优先使用对象级的属性 datePattern, 然后才是全局性的 defaultDatePattern
String dp = datePattern != null ? datePattern : getDefaultDatePattern();
if (dp != null) {
return "\"" + new SimpleDateFormat(dp).format(value) + "\"";
} else {
return "" + ((java.util.Date)value).getTime();
}
}
if(value instanceof Collection) {
return iteratorToJson(((Collection)value).iterator(), depth);
}
if(value instanceof Map) {
return mapToJson((Map)value, depth);
}
String result = otherToJson(value, depth);
if (result != null)
return result;
// 类型无法处理时当作字符串处理,否则ajax调用返回时js无法解析
// return value.toString();
return "\"" + escape(value.toString()) + "\"";
}
protected String otherToJson(Object value, int depth) {
if (value instanceof Character) {
return "\"" + escape(value.toString()) + "\"";
}
/*if (value instanceof Model) {
Map map = com.jfinal.plugin.activerecord.CPI.getAttrs((Model)value);
return mapToJson(map, depth);
}
if (value instanceof Record) {
Map map = ((Record)value).getColumns();
return mapToJson(map, depth);
}*/
if (value.getClass().isArray()) {
int len = Array.getLength(value);
List<Object> list = new ArrayList<Object>(len);
for (int i=0; i<len; i++) {
list.add(Array.get(value, i));
}
return iteratorToJson(list.iterator(), depth);
}
if (value instanceof Iterator) {
return iteratorToJson((Iterator)value, depth);
}
if (value instanceof Enumeration) {
ArrayList<?> list = Collections.list((Enumeration<?>)value);
return iteratorToJson(list.iterator(), depth);
}
if (value instanceof Enum) {
return "\"" + ((Enum)value).toString() + "\"";
}
return beanToJson(value, depth);
}
protected String beanToJson(Object model, int depth) {
return mapToJson(beanToMap(model,depth), depth);
}
public Map beanToMap(Object model, int depth){
return getBeanValue(model,depth);
}
/**
* 获取bean的无参get方法(包括is),从静态缓存里面取,若没有会反射取一次并存入缓存
* 这东西应该写个反射工具类
* @param model
* @return
*/
public List<MethodInfo> getBeanGetMethodFromCache(Object model){
if(this.useBeanCache){
List<MethodInfo> beanMethods = null;
beanMethods = beanGetMethodCache.get(model.getClass().getName());
if(beanMethods == null){
synchronized (this.getClass()) {
beanMethods = beanGetMethodCache.get(model.getClass().getName());
if(beanMethods == null){
beanMethods = reflectBeanGetMethod(model);
beanGetMethodCache.put(model.getClass().getName(), beanMethods);
}
return beanMethods;
}
}
return beanMethods;
}
return reflectBeanGetMethod(model);
}
/**
* 反射获取bean的get方法(包括is)
* @param model
* @return
*/
public List<MethodInfo> reflectBeanGetMethod(Object model){
Method[] ms = model.getClass().getMethods();
List<MethodInfo> beanMethods = new ArrayList<MethodInfo>();
for (Method m : ms) {
String methodName = m.getName();
int indexOfGet = methodName.indexOf("get");
if (indexOfGet == 0 && methodName.length() > 3) { // Only getter
String attrName = methodName.substring(3);
if (!attrName.equals("Class")) { // Ignore Object.getClass()
Class<?>[] types = m.getParameterTypes();
if (types.length == 0) {
beanMethods.add(new MethodInfo(attrName,m));
}
}
}else{
int indexOfIs = methodName.indexOf("is");
if (indexOfIs == 0 && methodName.length() > 2) {
String attrName = methodName.substring(2);
Class<?>[] types = m.getParameterTypes();
if (types.length == 0) {
beanMethods.add(new MethodInfo(attrName,m));
}
}
}
}
return beanMethods;
}
/**
* 获取bean的无参的get和is方法的值map
* @param model
* @param depth
* @return
*/
public Map getBeanValue(Object model, int depth){
Map map = new HashMap();
List<MethodInfo> methods = getBeanGetMethodFromCache(model);
for(int i = 0 ; i < methods.size(); i++){
try {
Object value = methods.get(i).getMethod().invoke(model);
map.put(StrKit.firstCharToLowerCase(methods.get(i).getFieldName()), value);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return map;
}
public <T> T parse(String jsonString, Class<T> type) {
throw new RuntimeException("默认 json 实现暂不支持 json 到 object 的转换");
}
public boolean isUseBeanCache() {
return useBeanCache;
}
public void setUseBeanCache(boolean useBeanCache) {
this.useBeanCache = useBeanCache;
}
}
增加类
/**
*
*/
public class MethodInfo {
private String fieldname;
private Method method;
public MethodInfo(String attrName, Method m) {
this.fieldname = attrName;
this.method = m;
}
public String getFieldName() {
return fieldname;
}
public void setFieldName(String name) {
this.fieldname = name;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
性能测试基本在1倍左右,若bean中有复杂的数据,性能差距会缩小。