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中有复杂的数据,性能差距会缩小。