公司采用的Teeda + Seasar的前后端Web开发框架。在对于Ajax这一块的支持上,一直感觉Teeda提供的Kumu框架比较难使,去网上查了一下关于如何直接使用jquery和Seasar进行ajax交互,也读了关于这一块的源码,记点心得。
先举个简单的例子。利用Dolteng插件(eclipse下该插件下载地址http://eclipse.seasar.org/updates/3.3/)建立一个Seasar web项目,并在view下新建sample.html:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:te="http://www.seasar.org/teeda/extension" xmlns:h="http://java.sun.com/jsf/html" xml:lang="ja" lang="ja"> <head> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" /> <script> $(function(){ $("#checkNickname").click( function() { var fields = new Object(); fields["component"] = "helloLogic"; fields["action"] = "ajaxNicknameCheck"; fields["AjaxParam1"] = "local"; fields["nickName"] = $("#nickName").val(); $.ajax({ type : "POST", url : "./my.ajax", data : fields, success : function(msg){ alert(msg); } }); }); }); </script> </head> <body> <form id="form"> <input type="text" id="nickName" /> <input type="button" id="checkNickname" value="check" /> </form> </body> </html>
这里我们写了一个ajax的例子,在点击check按钮时会去跟客户端交互检验nickName(当然现在我们后台代码还没有写)。但是按照例子所示,是可以直接用jquery提供的ajax函数(如果不了解jquery的$.ajax请自行查阅资料)和seasar后台交互。接下来我们结合源码来看看该如何在后台进行交互。
首先查看一下web.xml,有如下:
... <servlet> <servlet-name>ajaxServlet</servlet-name> <servlet-class>org.seasar.teeda.ajax.AjaxServlet</servlet-class> <load-on-startup>3</load-on-startup> </servlet> ... <servlet-mapping> <servlet-name>ajaxServlet</servlet-name> <url-pattern>*.ajax</url-pattern> </servlet-mapping> ...
Seasar为处理ajax专门实现了一个Servlet,负责接受任何指向*.ajax页面的请求。这也解释了sample.html中$.ajax的url为./my.ajax,实际上你可以定义任意的以ajax结尾的地址。
接下来看看AjaxServlet的代码:
... public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doAjax(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doAjax(request, response); } ...
核心代码在doAjax中,仔细分析该函数,首先是
String componentName = request .getParameter(AjaxConstants.REQ_PARAM_COMPONENT); String method = request.getParameter(AjaxConstants.REQ_PARAM_ACTION); if (method == null) { method = AjaxConstants.DEFAULT_AJAX_METHOD; } ...
其中AjaxConstants.REQ_PARAM_COMPONENT="component", AjaxConstants.REQ_PARAM_ACTION="action",这也解释了在sample.html中为何要在传入的参数中加入"component"和 "action"两个key。第一个是component名,第二个是要调用的该component的函数名。 接下来有:
... if (!method.startsWith(AJAX_PREFIX)) { MetaDef meta = def.getMetaDef(AjaxConstants.TEEDA_AJAX_META); if (meta == null) { throw new ServletException("Ajax Component Name[" + componentName + "] is not public."); } } ...
其中AJAX_PREFIX="ajax", 这意味着要调用的函数还必须是命名为以ajax开头。如sample.html中调用的ajaxNicknameCheck。 接下来:
... ComponentDef def = getComponentDefNoException(componentName); ... Object obj = def.getComponent(); ...
这个使用过seasar的童鞋再熟悉不过了,就是获取相应的component实例,不太了解的可以查阅http://dhongwu.iteye.com/blog/1949725。接下来:
... Object[] args = this.setRequestParameter(request, obj); ...
这个函数也比较重要,进入看看:
protected Object[] setRequestParameter(HttpServletRequest request, Object obj) { Object[] args = null; Map ajaxParam = new TreeMap(); Enumeration enume = request.getParameterNames(); while (enume.hasMoreElements()) { String key = (String) enume.nextElement(); String value = request.getParameter(key); if (key.startsWith(AjaxConstants.DEFAULT_ARRAY_PARAM_NAME)) { String index = key .substring(AjaxConstants.DEFAULT_ARRAY_PARAM_LENGTH); ajaxParam.put(new Integer(index), value); continue; } this.setPropertyNoException(obj, key, value); } int ajaxParamSize = ajaxParam.size(); if (0 < ajaxParamSize) { args = new Object[ajaxParamSize]; Iterator iterator = ajaxParam.keySet().iterator(); for (int i = 0; iterator.hasNext(); i++) { args[i] = ajaxParam.get(iterator.next()); } } return args; }
这个函数主要干了2件事。遍历所有request传入的参数,如果参数的name是形如"AjaxParam1", "AjaxParam2", "AjaxParam3"等的话(注:AjaxConstants.DEFAULT_ARRAY_PARAM_NAME="AjaxParam"),则把他们作为后台要执行的函数的参数。在sample.html中,定义了一个AjaxParam1="local",故"local"这个字符串将被作为ajaxNicknameCheck的参数。而作为执行函数参数的顺序由跟尾的数字决定。第2件事则是对于不符合AjaxParam开头的request的name,则把他对应的值注入到该component中的变量中。在本例中request有一对键和值("nickName", #nickName文本框的输入值),则在helloLogic这个component中定义一个public变量nickName(或者private变量nickName但是提供setNickName函数),就将被自动注入#nickName文本框的输入值。这一套和teeda本身的前后台注入是一样的。
了解了这么多,只需要在后台实现helloLogic这个component即可。假设工程root路径为jp.co.worksap.sample,那么新建package jp.co.worksap.sample.logic,新建接口HelloLogic:
public interface HelloLogic { public String ajaxNicknameCheck(String src); }
新建package jp.co.worksap.sample.logic.impl,新建类HelloLogicImpl:
public class HelloLogicImpl implements HelloLogic { public String nickName; public String ajaxNicknameCheck(String src) { //请自由发挥 ... } }
补充一句:关于为何HelloLogicImpl会被识别为名为"helloLogic"的component。这个在以前的文章中已经提及,请查阅http://dhongwu.iteye.com/admin/blogs/1949725。简而言之,请看下工程resources文件夹下的creator.dicon文件,里面定义了各种XXCreator,每一个Creator负责创建某个路径下的命名合法的类的实例。比如上文的jp.co.worksap.sample.logic.impl属于LogicCreator管理,然后HelloLogicImpl这个命名也符合规范,那么LogicCreator就会为HelloLogicImpl创建一个名为"helloLogic"的component。