[轻量级、零入侵、级联调用JSON-RPC for JAVA AJAX框架] |
|
JSON-RPC for Java2.5使用说明 |
目 录
概述
继《JavaScript高级应用与实践》(电子工业出版社.博文视点)之后推出的json-rpc-for-java开源代码,是仅仅100行的javascript代码和不到10个java文件实现的超级轻量级的通过 javaScript快速调用java对象并返回任意对象的轻量级框架,并且支持级联调用,也就是说不需要额外的JavaScript编程,就可以通过javascript调用被注册的java对象并返回java对象,如果被返回的对象还有方法,这个在javascript中返回的java对象的变量,还可以继续调用它的方法.....这就是这个轻量级json-rpc-for-java的神奇之处。
创意背景
发现其他的JSON-RPC开源的JavaScript代码太过繁杂,不够简洁,可维护性太差,而其注册复杂、配置繁多,使用不方便,并且不支持复杂对象的传入和级联调用功能。
应用前景和展望
该框架将封装即将发展多个扩展包:
1. 验证包:用于常规的Web开发验证使用,比如身份证、邮件地址、电话号码、邮编、期号等;
2. JavaScript功能包:包括UI、功能的对象封装,其好处是不在是常规的将JavaScript代码下载到浏览器进行使用,而是按需加载,从而降低网络流量,提高网络的浏览速度。
3. 其他服务包:包装google的在线翻译服务、图表服务等
术语
缩写 | 全称 | 描述 |
JSON | JavaScript Object Notation | JavaScript对象的一种字面量描述格式,是一种轻量级的数据交换格式——相对于XML,易于人阅读和编写,同时也易于机器解析 |
RPC | Remote procedure call | 远程过程(函数、方法)调用 |
AJAX | asynchronous JavaScript and XML | 狭义的解释是:异步的使用XML和JavaScript进行交互和通讯的一种技术;广义的而言,指一切Web上异步的技术 |
Java |
| 一种编程语言 |
JavaScript |
| 一种编程语言 |
开源项目地址
http://code.google.com/p/json-rpc-for-java/
工程svn下载地址
http://json-rpc-for-java.googlecode.com/svn/trunk/
不需要用户名和密码。
示例工程下载地址
http://json-rpc-for-java.googlecode.com/files/JsonRpcExample2008-08-05.rar
测试环境:MyEclipse、JRE1.4(或1.6)、tomcat 5.0(或6.0)如果你要测试,可以采用相应的环境,不一定要那么高版本的环境,Import工程后请注意修改工程中JRE为正确的本版路径,如下图所示:
环境
支持的浏览器
IE4、IE5、IE6、IE7、IE8、>= FireFox 2.0、>= Opera、>= Safari、google浏览器等等。力求设计与操作系统无关。
开发环境
Tomcat 6.0、Eclipse 3.2、JDK1.4。
运行环境
JRE1.4或以上的J2EE环境。
同类产品分析比较
更加灵活的注册方式
和DWR相比,它更加灵活,既能够通过web.xml静态为所有访问者注册JSON-RPC服务对象,还能动态通过代码进行注册,并且所有调用的方法不需要事先声明和注册。
支持级联调用和复杂对象作为入参
区别与其他JSON-RPC框架的是,它支持级联调用,对RPC的java服务方法,还支持传入复杂如Map、JavaBean这样的对象。
参数
Java服务方法入口参数类型
JavaScript类型到Java类型的对照如下,如果入口参数为复杂对象,则要求必须是能实例化的类型:
JavaScript对象 | Java对象 | 说明 |
String | java.lang.String | 字符串 |
java.lang.Object | Java复合类型,通常是JavaBean对象,或者是java.util.Map | 如果子元素不是简单类型,则遵循其他规则,否则以String处理,并且不支持深度嵌套的对象 |
number | java.util.Date java.sql.Timestamp | 如果调用参数是日期类型,则自动以数字进行处理:new Date(Long.parseLong(szTmp01)); |
java.lang.Boolean | Blooean | 对应的值:true、false |
String | java.lang.Character | 单引号的字符串,例如:‘c’ |
Number
| java.lang.Short、 java.lang.Integer、 java.lang.Long、 java.lang.Float、 java.lang.Double、 java.math.BigDecimal | JavaScript中都为数字对象,到参数里后自动转换类型 |
null | null | 空对象 |
|
|
|
如果转换失败,入口参数将是一个没有数据的入口参数对象,如果转换发生异常,则以null传入。
Java对象到JavaScript对象的对照表
调用异常时返回false。
Java对象 | JavaScript对象 | 说明 |
java.lang.String | String |
|
java.lang.Object | String | 调用java对象的toString()后转换到JavaScript里 |
java.util.Date、java.sql.Timestamp | String | 格式为yyyy-MM-dd HH:mm:ss.000,如果时分秒都为0,则为:yyyy-MM-dd |
java.lang.Boolean | Blooean | 对应的值:true、false |
java.lang.Character | String | 单引号的字符串,例如:‘c’ |
java.lang.Short、 java.lang.Integer、 java.lang.Long、 java.lang.Float、 java.lang.Double、 java.math.BigDecimal | Number | 到JavaScript中都为数字对象,可以直接参与加、减、乘、除运算 |
java.util.Map | Object | 例如:obj[“key1”]、obj[“key3”]、obj.key3,唯独没有以function作为属性的方法,当然,属于Object.prototype的function属性依然有的 |
java.util.List | Array | 例如:a[0]、a[2].getList() 也就是说List里也可以存在复合对象,这些对象依然可以有自己的方法 |
null | null | 空对象 |
其他Java对象 | Object | 例如:obj.displayName()、obj.aac001,可以有属性和方法 |
返回类型:
实现接口:
jcore.jsonrpc.common.face.IResultObject
这样,在JavaScript中不用再调用getErrMsg,直接使用你存放异常消息的变量名就可以获得异常、错误消息。
实例化:
或者在你的异步服务方法中实例化:
jcore.jsonrpc.common.ResultObject
调用ResultObject.setResult将你实际返回的对象set进去,这样,当发生异常消息的时候可以在js中用如下的方式获取
// 错误消息,和结果一次性取出,而不用调用getErrMsg
// 因为当调用getErrMsg的时候就会再次发起异步请求
var szMsg = rpcRstObj['errMsg'],
// 这是返回的对象
oRst = rpcRstObj['result']
;
注意事项
如果你的java服务对象返回的是Object、Bean、Map或者自定义对象,不能有属性_name_、_id_,这两个属性被本框架内部使用;
功能介绍
自动捕获异常
在你编写的java服务类的方法中不需要try{….}catch(Exceptione){},本框架会为你捕获异常错误消息,当你在javascript中没有获取到正确的数据,可以调用异步对象的方法getErrMsg()获取异常消息,该方法封装在jcore.jsonrpc.common.JsonRpcObject中,也就是AJAX服务java基类中。
当返回类型为:
jcore.jsonrpc.common.ResultObject
jcore.jsonrpc.common.face.IResultObject
则可以只用通过返回的对象中获取oRst['errMsg']异常消息。
JavaScript中释放注册的Java服务对象
你只需要在JavaScript中调用release()就可以释放注册的Java对象资源,详细见示例工程,或者见:
http://code.google.com/p/json-rpc-for-java/wiki/Wiki32
级联调用功能
不明白的地方请结合示例工程进行理解。
1、Java中注册复合对象myjsonrpc
2、JSPJavaScript中获取该对象:var myjsonrpc= JsonRpcClient().myjsonrpc;
3、调用被注册的java对象的方法getMyObj,返回复合的java对象TestDomain:
var oDomain = myjsonrpc. getMyObj();
// 继续调用该返回的java对象的方法
alert(oDomain. toXml())
或者:alert(myjsonrpc.getList()[1].toXml());
如果toXml返回的还是一个复合的Java对象,你可以继续在JavaScript中继续调用,而不需要额外的编程。
按需加载JavaScript库
Base
方法名 | 说明 | 使用举例 |
A(a) | 转换有length属性的对象为Array。将参数a对象转换为有效的Array对象;a参数必须为有效的拥有length属性的对象 | var json = JsonRpcClient("JRPC"); var o = json.LoadJsObj("Base"); o.id("myDivId");// 获取对象 // 自动保存滚动条的位置 o.autoSaveScroll("myTextArea"); |
id(s) | 根据id获取对象。如果参数s为String,则以他为id获取对象并返回,否则返回s | |
addEvent(o,t,f) | 给对象绑定事件处理。 o:为HTML对象 t:为事件名,例如click、focus f:为事件处理函数 | |
getCookie(k) | 获取名字为k的cookie值 | |
setCookie(k,v) | 将名字为k,值为v的键值对设置到cookie;如果v为无效的值,则删除该项cookie | |
autoSaveScroll(o) | 设置对象o为自动保存滚动条位置——人性化设计 | |
clearScroll(o) | 通常,在提交数据后不需要保持对象的滚动条位置,因此需要调用该方法在提交数据前清楚滚动条存储信息 |
使用
Web.xml配置
需要在web.xml中加入下面的配置
<servlet> <servlet-name>JSONRPCServlet</servlet-name> <servlet-class> jcore.jsonrpc.servlet.JSONRPCServlet </servlet-class>
<!— 这里注册的对象将对所有访问者起作用 <init-param> <param-name>regAppClassNames</param-name> <param-value> 注册这些对象,让所有的web用户都能使用这些JSON-RPC java对象 这些对象会复制到每个用户的session对象中,因此他们必须实现接口Serializable 以分号作为分割符号 myTest:jcore.jsonrpc.test.Test; myTrs:jcore.jsonrpc.test.A </param-value> </init-param> -->
<load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JSONRPCServlet</servlet-name> <url-pattern>/JRPC</url-pattern> </servlet-mapping> |
引入Jar包
需要在工程中引入:JSON-RPC.jar、commons-logging.jar、commons-logging-api.jar,其中后面两个jar在示例工程中的JsonRpcExample\webapp\WEB-INF\lib\下。示例工程下载地址:
http://json-rpc-for-java.googlecode.com/files/JsonRpcExample2008-08-05.rar
而,JSON-RPC.jar,你也可以引入源代码重新进行打包。
AJAX服务Java类的编写
JsonRpcObject基类中的方法列表
方法名 | 说明 |
getErrMsg | 在javaScript中调用获取异常、错误消息使用 |
getRequest | 在java服务的jcore.jsonrpc.common.JsonRpcObject继承子类中使用,获取到request对象,以便获取session等对象 |
setErrMsg | 同上,在继承的子类中设置错误或异常消息的方法;该方法是框架抓取异常消息填写的方法 |
setRequest | 框架系统使用,注入request的地方 |
服务类示例
必须继承与jcore.jsonrpc.common.JsonRpcObject,并实现接口java.io.Serializable。
例如示例工程中的AJAX服务Java类:
package test; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map;
import jcore.jsonrpc.common.JsonRpcObject;
publicclass TestObjectextendsJsonRpcObject implementsSerializable{ privatestaticfinallongserialVersionUID = 1L;
private ListmyList = new ArrayList(); private Mapmap = new HashMap();
public TestObject() { myList.add("good"); myList.add(new TestDomain()); // map中也可以放入复合对象 map.put("first","第一条值"); map.put("p2",new Date()); map.put("domain",myList.get(1)); }
/*** *返回Map对象 *@return */ public Map getMap() { returnmap; }
/*** *获取一个普通对象 *@return */ public Object getStr() { returnmyList.get(0); }
/*** *获取一个复合对象 *@return */ public Object getMyObj() { returnmyList.get(1); }
/*** *获取List对象 *@return */ public List getList() { returnmyList; } }
|
自己基类的编写
同样,你可以继承jcore.jsonrpc.common.JsonRpcObject实现一些基类,这样在自己的项目中更加方便实用,例如:
package com.yinhai.yhsi2.web.common;
import com.yinhai.webframework.session.UserSession; import jcore.jsonrpc.common.JsonRpcObject;
public abstract class Yhsi2JsonRpcObj extends JsonRpcObject {
private UserSession us = null; public Yhsi2JsonRpcObj() { super(); }
public UserSession getUs() { if(null == us) us = UserSession.getUserSession(getRequest()); return us; } } |
注意事项
RPC的Java服务类返回的对象不能有成员变量引用自身对象,服务类对象不能有成员变量为Service或BPO对象,因为这些对象内部通常有一个成员变量引用了自身,这会导致服务类无法正常转换为正确的JavaScript对等的应用类,会导致堆栈溢出的控制台异常错误信息——我们预计在下一版本中增强这样的自身引用的检测并加以回避。
AJAX服务Java类的注册
// 注意,被注册的类必须是能被实例化的类
jcore.jsonrpc.common.JsonRpcRegister.registerObject(us,"myjsonrpc", test.TestObject.class);
使用test.TestObject.class的方式是保证多次注册不至于test.TestObject被多次注册而执行多次实例化,从而提高性能,并允许多次注册——实际上内部只注册了一次。
自己注册基类的编写
当然,你也可以继承jcore.jsonrpc.common.JsonRpcRegister以便使得在应用菜单切换的时候释放资源,例如:
package com.yinhai.yhsi2.web.common; import javax.servlet.http.HttpServletRequest;
import jcore.jsonrpc.common.Content; import jcore.jsonrpc.common.JSONRPCBridge;
import com.yinhai.webframework.session.UserSession; import jcore.jsonrpc.common.JsonRpcRegister;
/*** * 注册JsonRpc对象 * @author just * */ public class JsonRpcRegister extends jcore.jsonrpc.common.JsonRpcRegister{
/*** * 通过request来注册对象 * @param request * @param szKeyName * @param o */ public static void registerObject(HttpServletRequest request, String szKeyName, Object o) { registerObject(UserSession.getUserSession(request), szKeyName, o); }
/*** * 通过request来注册对象 * @param request * @param szKeyName * @param o */ public static void registerObject(UserSession us, String szKeyName, Object o) { if(null != us) { JSONRPCBridge brg = (JSONRPCBridge)us.getCurrentBusiness().getSessionResource(Content.RegSessionJSONRPCName); // 如果是第一次就注册对象 if(null == brg) us.getCurrentBusiness().putSessionResource(Content.RegSessionJSONRPCName, brg = new JSONRPCBridge().setSession(us.getHttpSession())); brg.registerObject(szKeyName, o); } }
/*** * 通过request来注册对象 * @param request * @param szKeyName * @param o */ public static void registerObject(HttpServletRequest request, String szKeyName, Class o) { registerObject(UserSession.getUserSession(request), szKeyName, o); }
/*** * 通过request来注册对象 * @param request * @param szKeyName * @param o */ public static void registerObject(UserSession us, String szKeyName, Class o) { if(null != us) { JSONRPCBridge brg = (JSONRPCBridge)us.getCurrentBusiness().getSessionResource(Content.RegSessionJSONRPCName); // 如果是第一次就注册对象 if(null == brg) us.getCurrentBusiness().putSessionResource(Content.RegSessionJSONRPCName, brg = new JSONRPCBridge().setSession(us.getHttpSession())); try { brg.registerObject(szKeyName, o.newInstance()); } catch (InstantiationException e) { } catch (IllegalAccessException e) { } } } } |
银海公司专用服务类示例
package com.yinhai.yhcip.common;
import java.util.Map;
import jcore.jsonrpc.common.JsonRpcObject;
import com.yinhai.sysframework.persistence.IDao; import com.yinhai.sysframework.service.ServiceLocator;
/** * Rpc调用 * @author just * */ public class Rpcyhcip extends JsonRpcObject{
/** * 远程异步调用过程 * @param szDaoId dao名称id * @param szSqlId sql语句id * @param map 入口参数 * @return */ public boolean delete(String szDaoId, String szSqlId, Map map) { try { // 如果需要UserSession对象:UserSession us = UserSession.getUserSession(this.getRequest()); // getRequest方法是父类中实现 IDao dao =(IDao) ServiceLocator.getInstance().getService(szDaoId); return 0 < dao.delete(szSqlId, map); }catch(Exception e) { e.printStackTrace(); setErrMsg(e.getMessage()); } return false; } } |
JSP中的使用
引入JsonRpcClient.js
<script charset="UTF-8"type="text/JavaScript" src="JsonRpcClient.js"></script>
最新的文件可以从http://code.google.com/p/json-rpc-for-java/
下载
调用
<script charset="UTF-8" type="text/JavaScript"><!--//--><![CDATA[//><!--
// myjsonrpc就是通过JsonRpcRegister.registerObject注册的名字 // 这时候这里的rpc就拥有了通过JsonRpcRegister.registerObject注册的 // 异步对象的相应方法了 var rpc = JsonRpcClient().myjsonrpc; // 传入个人编号获取人的基本信息并填充到界面上 if("dto(aac001)" === o.name && 0 === "aab001".getValue().length) { if(0 < o.value.length) { // 获取到的myjsonrpc同样有aac001,aab001等等属性, // 你可以直接使用,同样有getAac001()等方法, // 可以直接使用,而不需要额外的编码 var myjsonrpc = rpc.getEmployeeBaseInfo(o.value), errMsg = rpc.getErrMsg(); if(0 < errMsg.length) return o.focus(), alert(errMsg), false; for(var k in myjsonrpc) if(6 === k.length && myjsonrpc[k]) k.getObj() && k.setValue(myjsonrpc[k]); fnSetAtbt("dto(aac001)".getObj(),1), fnSetAtbt("dto(aab001)".getObj(),4), "aab001".focus(); // 关闭错误消息提示 ; } } // 2008-08-02 增加自动拦截异常消息功能,因此在你写的代码中不需要编写try catch // 如果有异常消息,可以在js中调用rpc.getErrMsg()获得 // 如果需要释放被注册名为myjsonrpc的对象JsonRpcObj,可以在js中调用rpc.release(); // 2008-08-13 增加对FireFox 3.01的支持,增加传入JavaBean、Map参数的支持 //--><!]]></script> |
如果本调用的方法getEmployeeBaseInfo的第一个参数是function,则把它作为异步的回调函数。
调用未注册和配置的类方法
1、 首先,被调用的类需要继承jcore.jsonrpc.common.JsonRpcObject或实现接口jcore.jsonrpc.common.face.IjsonRpcObject,并有默认的构造函数;
例如:
package test.rpc;
import jcore.jsonrpc.common.JsonRpcObject;
public class MyTestRpc extends JsonRpcObject{
/**
* 调用:rpc.getRpcObj('test.rpc.MyTestRpc').getTestMsg()
* @return
*/
publicString getTestMsg()
{
return"噢,成功了!";
}
}
2、 JSP的JavaScript中调用的方式,例如:alert(rpc.getRpcObj('test.rpc.MyTestRpc').getTestMsg());