在前面的新闻系统设计中,常常会用到有关ajax的请求,如对文章类别的动态调用.本来struts2提供了一个json的插件,不过在开发的时候还不知道.自己写了一个,还将就吧.
基本原理,就是改变struts2的返回类型,原来的返回类型为string,如果返回类型为void即不返回任何类型,而改由struts2的的ServletContext对象获得用到输出的out对象,将json对象输出到页面上,同样可以达到ajax格式效果.最后在页面上作了小小的操作,将json格式的字符串变成js对象进行调用,以完成相应操作.流程如下:
- 获得response对象,进行获得printWriter对象
- 将想要返回给页面上的对象转化成json格式的字符串
- 由printWriter将json字符串写到页面输出流(通常是被js来获得结果)
- 页面处理json字符串,转化成js对象,传入回调函数进行调用
这里主要用到几个对象ServletContext(这个是struts2的工具对象,可获得request,response等对象),JsonUtil(自己写的一个类,用到转化对象),protype.js(用到提交ajax请求,接收返回结果),此外,还有一个jsonUtil的辅助类BeanUtil(用到将javaBean转化成map对象).这里主要贴出jsonUtil的转换方法和BeanUtil的辅助方法,看这种方式是否合适.
- public class JsonUtil {
- public JsonUtil() {}
- private static boolean isSimpleType(Class clazz) {
- return clazz.isPrimitive() || String.class.isAssignableFrom(clazz) || Number.class.isAssignableFrom(clazz)
- || java.util.Date.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz)
- || Character.class.isAssignableFrom(clazz);
- }
- public static String convertToJson(Object obj) {
- JsonHandle jsonHandle = new JsonHandle();
- return jsonHandle.convertToJson(0, obj).toString();
- }
- private static class JsonHandle {
- public static final int DEFAULT_DEPTH = 10;
- public StringBuffer convertToJson(int depth, Object obj) {
- StringBuffer sb = new StringBuffer();
- if(obj == null) {
- sb.append("null");
- } else if(isSimpleType(obj.getClass())) {
- if((obj instanceof String) || (obj instanceof java.util.Date)) {
- sb.append("\"").append(obj).append("\"");
- } else if(obj instanceof Character) {
- sb.append("'").append(obj).append("'");
- } else {
- sb.append(obj);
- }
- } else if(obj instanceof Collection) {
- boolean hibernateFlag;
- try {
- ((Collection) obj).size();
- hibernateFlag = true;
- } catch(Exception ex) {
- hibernateFlag = false;
- }
- if(hibernateFlag) {
- sb.append("[");
- for(Iterator iterator = ((Collection) obj).iterator(); iterator.hasNext();) {
- sb.append(convertToJson(depth, iterator.next()));
- if(iterator.hasNext()) {
- sb.append(",");
- }
- }
- sb.append("]");
- } else {
- sb.append("null");
- }
- } else if(obj.getClass().isArray()) {
- sb.append("[");
- int max = java.lang.reflect.Array.getLength(obj);
- for(int i = 0; i < max; i++) {
- if(i > 0) {
- sb.append(",");
- }
- sb.append(convertToJson(depth, java.lang.reflect.Array.get(obj, i)));
- }
- sb.append("]");
- } else if(java.util.Map.class.isAssignableFrom(obj.getClass())) {
- sb.append("{\n");
- for(Map.Entry e : ((Map) obj).entrySet()) {
- if(!(e.getKey() instanceof String))//如果键值不是字符串,则跳过.
- continue;
- sb.append(e.getKey()).append(":");
- if(depth <= DEFAULT_DEPTH) {
- sb.append(convertToJson(depth + 1, e.getValue()));
- } else {
- sb.append("undefined");
- }
- sb.append(",");
- }
- if(sb.length() > 3) {
- sb.deleteCharAt(sb.length() - 1);
- }
- sb.append("\n}");
- } else {
- Map map = null;
- try {
- map = BeanUtil.getPropertiesByReflect(obj);
- } catch(Exception ex) {
- ex.printStackTrace();
- }
- sb.append(convertToJson(depth, map));
- }
- return sb;
- }
- }
- }
以及辅助类:
- public class BeanUtil {
- /** Creates a new instance of BeanUtil */
- private BeanUtil() {
- }
- public static Map getPropertiesByReflect(Object obj) throws Exception {
- if(obj == null)
- return null;
- Field[] fields = obj.getClass().getDeclaredFields();
- if(fields == null || fields.length == 0)
- return null;
- Map map = new HashMap();
- AccessibleObject.setAccessible(fields, true);
- for(Field field : fields) {
- if(!field.getName().equals("serialVersionUID") && field.getAnnotation(Fly_m.class) == null)
- map.put(field.getName(), field.get(obj));
- }
- if(map.size() < 1)
- return null;
- return map;
- }
- }
在JsonUtil里,主要对一个对象进行分析,采取不同的策略对不同类型的对象进行递归性地转化.对应集合类型,采用迭代地方式.而进行通用的javaBean对象,则先将其转化成一个map对象,然后再进行转化.在本程序中,用到了一个转化深度地概念,主要是防止某些类地无限属性或者递归属性.考虑到并不需要将对象中的每一个属性进行转化,这里引入了一个相当于Traisient的注释@Fly_m,作用到字段上(因为对javaBean的解析是以反射来解析的).如果解析的属性存在这个注解,则被忽略.(一开始想要transient的modifier属性,发现与hibernate相冲突(hiberante对于transient的属性也不会解析?).
#-----------------------------
在客户端,我采用protype.js提供的基本的ajax调用方法Ajax.Request(action,{method,parameters,onComplete,onException)这种访求进行服务器请求,同时写了一个小小的辅助js进行调用,以方便页面调用.Fly_m.js,使用这个js后,通常的调用被简单化为m_ylf.invoke(actionName,method,parameters,callBack).这样,只需要四个参数,同时内置调试参数设置,以及传递给callBack对象的不再是一个xmlHttpRequest对象,而是一个已经eval好的js对象,让使用更加方便.源码如下:
- var m_ylf = function() {};
- m_ylf.defaultPath = "js";
- m_ylf.preDebug = false;
- m_ylf.postDebug = false;
- m_ylf.prefix="";
- m_ylf.setPrefix = function(pre) {
- m_ylf.prefix = pre;
- }
- m_ylf.setPreDebug = function(pre){
- m_ylf.preDebug = pre;
- }
- m_ylf.setPostDebug = function(post) {
- m_ylf.postDebug = post;
- }
- m_ylf.handleException = function(exception) {
- window.alert("Error -> " + exception.message);
- }
- m_ylf.invoke = function(actionName, param, callBack) {
- if(arguments.length != 3)
- return;
- if(typeof callBack != "function") {
- callBack = function() {};
- }
- if(actionName == null || actionName == "")
- return;
- var params = null;
- if(param == null || param == "")
- params = "";
- else
- params = param;
- if(m_ylf.preDebug)
- alert(params);
- var action = actionName + ".p";
- if(m_ylf.prefix != null && m_ylf.prefix != "")
- action = m_ylf.prefix + action;
- new Ajax.Request(action,
- {method:"get",
- parameters:params,
- onComplete:function(data) {
- if(m_ylf.postDebug) {
- window.alert(data.responseText);
- }
- var temp = "(" + data.responseText + ")";
- temp = eval(temp);
- callBack(temp);
- },
- onException:m_ylf.handleException
- });
- }
不过有个小小的缺点,就是这个js只是符合我自己的系统设计,如果想要通用些,就需要改变其中第44行的action定义,不过我想这通常没有再大的问题吧.preDebug属性可查看请求参数,而postDebug可查看返回给客户端的json字符串,以进行相关的结果跟踪.
我想这个小小的json转化类帮助一些小的开发还行,大的开发就不行了,因为可以涉及到对象的解析,以及生成json字符串的速度.不过对于要求不太高的应用还是足够了.而且页面上的请求都是最基本的请求,完全可以在基础之上进行添加新的功能.以后有想法再在上面作些文章.
相关源码放在附件中,需要的同学们可下来看看.