zhipingch 原创
去年初,正好负责一个医药信息系统的设计开发,架构设计时,采用struts+jdbc(自定义采用适配器模式封装了hashmap动态vo实现的持久层)。后来ajax热潮兴起,正好系统中有很多地方需要和服务器端交互数据,如采购销售系统中的订单头/订单明细等主从表结构的维护。
[color=blue]数据交互过程[/color],我们考虑采用xml来组织数据结构,更新/保存:前台封装需要的xml,通过ajax提交---〉action解析xml ---〉改造原有的持久层实现xml持久化;
查询时:持久层根据实际需要返回xml,document对象,---〉action 处理 --〉前台自己封装js库来解析xml,并刷新部分页面。
ajax:已经有很多方法实现跨浏览器的方式,这里只介绍最简单的方式,同步模式下提交xmlstr给action(*.do)。
- /**
- * 将数据同步传递给后台请求url
- * @return 返回xmlhttp 响应的信息
- * @param-url = '/web/module/xxx.do?p1=yy&p2=rr';
- * @param-xmlstr:xml格式的字符串 <data><xpath><![cdata[数据信息]]></xpath></data>
- * @author zhipingch
- * @date 2005-03-17
- */
- function senddata(urlstr, xmlstr) {
- var xmlhttp = new activexobject("microsoft.xmlhttp");
- xmlhttp.open("post", urlstr, false);
- xmlhttp.setrequestheader("content-type", "application/x-www-form-urlencoded");
- if (xmlstr) {
- xmlhttp.send(xmlstr);
- } else {
- xmlhttp.send();
- }
- return xmlhttp.responsexml;
- }
struts中我们扩展了action,实现了xmlstr转化成document对象(dom4j),并且完善了转发方式。如
[quote]
1.dispatchaction
以一个controller响应一组动作绝对是controller界的真理,struts的dispatchaction同样可以做到这点。
[list]
<action path="/admin/user" name="userform" scope="request" parameter="method" validate="false">
<forward name="list" path="/admin/userlist.jsp"/>
</action>
[/list]
其中parameter="method" 设置了用来指定响应方法名的url参数名为method,即/admin/user.do?method=list 将调用useraction的public actionforward list(....) 函数。
public actionforward unspecified(....) 函数可以指定不带method方法时的默认方法。[/quote]
但是这样需要在url后多传递参数[size=18][color=red]method=list [/color][/size];并且action节点配置中的[color=red]parameter="method" [/color]
也没有被充分利用,反而觉得是累赘!
因此我们直接在basedispatchaction中增加xml字符串解析,并充分利用action节点配置中的[color=red]parameter="targetmethod" [/color],使得转发的时候,action能够直接转发到子类的相应方法中,减少了url参数传递,增强了配置信息可读性,方便团队开发。
同样以上述为例,扩展后的配置方式如下:
[quote]
<action path="/admin/user" scope="request" [color=red]parameter="list"[/color] validate="false">
<forward name="list" path="/admin/userlist.jsp"/>
</action>
[/quote]
其中[color=red]parameter="list"[/color] 设置了用来指定响应url=/admin/user.do的方法名,它将调用useraction的public actionforward list(....) 函数。
basedispatchdocumentaction 的代码如下,它做了三件重要的事情:
1、采用dom4j直接解析xml字符串,并返回document,如果没有提交xml数据,或者采用form形式提交的话,返回null;
2、采用模版方法处理系统异常,减少了子类中无尽的try{...}catch(){...};其中异常处理部分另作描述(你可以暂时去掉异常处理,实现xml提交和解析,如果你有兴趣,我们可以进一步交流);
| |
3、提供了spring配置bean的直接调用,虽然她没有注入那么优雅,但是实现了ajax、struts、spring的结合。
basedispatchdocumentaction 的源码如下:
- package com.ufida.haisheng.struts;
- import java.io.ioexception;
- import java.io.printwriter;
- import java.lang.reflect.invocationtargetexception;
- import java.lang.reflect.method;
- import java.math.bigdecimal;
- import java.sql.timestamp;
- import java.util.date;
- import java.util.hashmap;
- import javax.servlet.http.httpservletrequest;
- import javax.servlet.http.httpservletresponse;
- import javax.servlet.http.httpsession;
- import org.apache.commons.beanutils.convertutils;
- import org.apache.commons.beanutils.converters.bigdecimalconverter;
- import org.apache.commons.beanutils.converters.classconverter;
- import org.apache.commons.beanutils.converters.integerconverter;
- import org.apache.commons.beanutils.converters.longconverter;
- import org.apache.log4j.logger;
- import org.apache.struts.action.action;
- import org.apache.struts.action.actionform;
- import org.apache.struts.action.actionforward;
- import org.apache.struts.action.actionmapping;
- import org.apache.struts.util.messageresources;
- import org.dom4j.document;
- import org.dom4j.io.saxreader;
- import org.hibernate.hibernateexception;
- import org.springframework.beans.beansexception;
- import org.springframework.context.applicationcontext;
- import org.springframework.dao.dataaccessexception;
- import org.springframework.web.context.support.webapplicationcontextutils;
- import com.ufida.haisheng.constants.globals;
- import com.ufida.haisheng.converter.dateconverter;
- import com.ufida.haisheng.converter.timestampconverter;
- import com.ufida.haisheng.exp.exceptiondto;
- import com.ufida.haisheng.exp.exceptiondisplaydto;
- import com.ufida.haisheng.exp.exceptionhandler.exceptionhandlerfactory;
- import com.ufida.haisheng.exp.exceptionhandler.exceptionutil;
- import com.ufida.haisheng.exp.exceptionhandler.iexceptionhandler;
- import com.ufida.haisheng.exp.exceptions.baseappexception;
- import com.ufida.haisheng.exp.exceptions.mappingconfigexception;
- import com.ufida.haisheng.exp.exceptions.nosuchbeanconfigexception;
- /**
- * 系统的ajax转发基类。增加模版处理异常信息。
- *
- * @author 陈志平 chenzp
- * @desc basedispatchdocumentaction.java
- *
- * @说明: web 应用基础平台
- * @date 2005-03-02 11:18:01 am
- * @版权所有: all right reserved 2006-2008
- */
- public abstract class basedispatchdocumentaction extends action {
- protected class clazz = this.getclass();
- protected static logger log = logger.getlogger(basedispatchdocumentaction.class);
- /**
- * 异常信息
- */
- protected static threadlocal<exceptiondisplaydto>
expdisplaydetails = new threadlocal<exceptiondisplaydto>(); - private static final long defaultlong = null;
- private static applicationcontext ctx = null;
- /**
- * 注册转换的工具类 使得from中的string --
- * model中的对应的类型(date,bigdecimal,timestamp,double...)
- */
- static {
- convertutils.register(new classconverter(), double.class);
- convertutils.register(new dateconverter(), date.class);
- convertutils.register(new dateconverter(), string.class);
- convertutils.register(new longconverter(defaultlong), long.class);
- convertutils.register(new integerconverter(defaultlong), integer.class);
- convertutils.register(new timestampconverter(), timestamp.class);
- convertutils.register(new bigdecimalconverter(defaultlong), bigdecimal.class);
- }
- /**
- * the message resources for this package.
- */
- protected static messageresources messages = messageresources.getmessageresources("org.apache.struts.actions.localstrings");
- /**
- * the set of method objects we have introspected for this class, keyed by
- * method name. this collection is populated as different methods are
- * called, so that introspection needs to occur only once per method name.
- */
- protected hashmap<string, method> methods = new hashmap<string, method>();
- /**
- * the set of argument type classes for the reflected method call. these are
- * the same for all calls, so calculate them only once.
- */
- protected class[] types = { actionmapping.class, actionform.class, document.class, httpservletrequest.class,
- httpservletresponse.class };
- /**
- * process the specified http request, and create the corresponding http
- * response (or forward to another web component that will create it).
- * return an <code>actionforward</code> instance describing where and how
- * control should be forwarded, or <code>null</code> if the response has
- * already been completed.
- *
- * @param mapping
- * the actionmapping used to select this instance
- * @param form
- * the optional actionform bean for this request (if any)
- * @param request
- * the http request we are processing
- * @param response
- * the http response we are creating
- *
- * @exception exception
- * if an exception occurs
- */
- public actionforward execute(actionmapping mapping, actionform form, httpservletrequest request,
- httpservletresponse response) throws exception {
- response.setcontenttype("text/html; charset=utf-8");
- exceptiondisplaydto expdto = null;
- try {
- document doc = createdocumentfromrequest(request);
- /*
- * 这里直接调用mapping的parameter配置
- */
- string actionmethod = mapping.getparameter();
- /*
- * 校验配置的方法是否正确、有效
- */
- isvalidmethod(actionmethod);
- return dispatchmethod(mapping, form, doc, request, response, actionmethod);
- } catch (baseappexception ex) {
- expdto = handlerexception(request, response, ex);
- } catch (exception ex) {
- exceptionutil.logexception(this.getclass(), ex);
- rendertext(response,"[error :对不起,系统出现错误了,请向管理员报告以下异常信息./n" + ex.getmessage() + "]");
- request.setattribute(globals.errormsg, "对不起,系统出现错误了,请向管理员报告以下异常信息./n" + ex.getmessage());
- expdto = handlerexception(request,response, ex);
- } finally {
- expdisplaydetails.set(null);
- }
- return null == expdto ? null : (expdto.getactionforwardname() == null ? null : mapping.findforward(expdto.getactionforwardname()));
- }
- /**
- * 直接输出纯字符串
- */
- public void rendertext(httpservletresponse response, string text) {
- printwriter out = null;
- try {
- out = response.getwriter();
- response.setcontenttype("text/plain;charset=utf-8");
- out.write(text);
- } catch (ioexception e) {
- log.error(e);
- } finally {
- if (out != null) {
- out.flush();
- out.close();
- out = null;
- }
- }
- }
- /**
- * 直接输出纯html
- */
- public void renderhtml(httpservletresponse response, string text) {
- printwriter out = null;
- try {
- out = response.getwriter();
- response.setcontenttype("text/html;charset=utf-8");
- out.write(text);
- } catch (ioexception e) {
- log.error(e);
- } finally {
- if (out != null) {
- out.flush();
- out.close();
- out = null;
- }
- }
- }
- /**
- * 直接输出纯xml
- */
- public void renderxml(httpservletresponse response, string text) {
- printwriter out = null;
- try {
- out = response.getwriter();
- response.setcontenttype("text/xml;charset=utf-8");
- out.write(text);
- } catch (ioexception e) {
- log.error(e);
- } finally {
- if (out != null) {
- out.flush();
- out.close();
- out = null;
- }
- }
- }
- /**
- * 异常处理
- * @param request
- * @param out
- * @param ex
- * @return exceptiondisplaydto异常描述对象
- */
- private exceptiondisplaydto handlerexception(httpservletrequest request,httpservletresponse response, exception ex) {
- exceptiondisplaydto expdto = (exceptiondisplaydto) expdisplaydetails.get();
- if (null == expdto) {
- expdto = new exceptiondisplaydto(null,this.getclass().getname());
- }
- iexceptionhandler exphandler = exceptionhandlerfactory.getinstance().create();
- exceptiondto exdto = exphandler.handleexception(expdto.getcontext(), ex);
- request.setattribute("exceptiondto", exdto);
- rendertext(response,"[error:" + (exdto == null ? "exceptiondto is null,请检查expinfo.xml配置文件." : exdto.getmessagecode())
- + "]");
- return expdto;
- }
- private void isvalidmethod(string actionmethod) throws mappingconfigexception {
- if (actionmethod == null || "execute".equals(actionmethod) || "perform".equals(actionmethod)) {
- log.error("[basedispatchaction->error] parameter = " + actionmethod);
- expdisplaydetails.set(new exceptiondisplaydto(null, "mappingconfigexception"));
- throw new mappingconfigexception("对不起,配置的方法名不能为 " + actionmethod);
- }
- }
- /**
- * 解析xml流
- * @param request
- * @return document对象
- */
- protected static document createdocumentfromrequest(httpservletrequest request) throws exception {
- try {
- request.setcharacterencoding("utf-8");
- document document = null;
- saxreader reader = new saxreader();
- document = reader.read(request.getinputstream());
- return document;
- } catch (exception ex) {
- log.warn("tips:没有提交获取xml格式数据流! ");
- return null;
- }
- }
- /**
- * dispatch to the specified method.
- *
- * @since struts 1.1
- */
- protected actionforward dispatchmethod(actionmapping mapping, actionform form, document doc,httpservletrequest request,
httpservletresponse response, string name) throws exception { - method method = null;
- try {
- method = getmethod(name);
- } catch (nosuchmethodexception e) {
- string message = messages.getmessage("dispatch.method", mapping.getpath(), name);
- log.error(message, e);
- expdisplaydetails.set(new exceptiondisplaydto(null, "mappingconfigexception"));
- throw new mappingconfigexception(message, e);
- }
- actionforward forward = null;
- try {
- object args[] = { mapping, form, doc, request, response };
- log.debug("[execute-begin] -> " + mapping.getpath() + "->[" + clazz.getname() + "->" + name + "]");
- forward = (actionforward) method.invoke(this, args);
- log.debug(" [execute-end] -> " + (null == forward ? "use ajax send to html/htm" : forward.getpath()));
- } catch (classcastexception e) {
- string message = messages.getmessage("dispatch.return", mapping.getpath(), name);
- log.error(message, e);
- throw new baseappexception(message, e);
- } catch (illegalaccessexception e) {
- string message = messages.getmessage("dispatch.error", mapping.getpath(), name);
- log.error(message, e);
- throw new baseappexception(message, e);
- } catch (invocationtargetexception e) {
- throwable t = e.gettargetexception();
- string message = messages.getmessage("dispatch.error", mapping.getpath(), name);
- throw new baseappexception(message, t);
- }
- return (forward);
- }
- /**
- * introspect the current class to identify a method of the specified name
- * that accepts the same parameter types as the <code>execute</code>
- * method does.
- *
- * @param name
- * name of the method to be introspected
- *
- * @exception nosuchmethodexception
- * if no such method can be found
- */
- protected method getmethod(string name) throws nosuchmethodexception {
- synchronized (methods) {
- method method = (method) methods.get(name);
- if (method == null) {
- method = clazz.getmethod(name, types);
- methods.put(name, method);
- }
- return (method);
- }
- }
- /**
- * 返回spring bean对象
- * @param name spring bean的名称
- * @exception baseappexception
- */
- protected object getspringbean(string name) throws baseappexception {
- if (ctx == null) {
- ctx = webapplicationcontextutils.getwebapplicationcontext(this.getservlet().getservletcontext());
- }
- object bean = null;
- try {
- bean = ctx.getbean(name);
- } catch (beansexception ex) {
- throw new nosuchbeanconfigexception("对不起,您没有配置名为:" + name + "的bean。请检查配置文件!", ex.getrootcause());
- }
- if (null == bean) {
- throw new nosuchbeanconfigexception("对不起,您没有配置名为:" + name + "的bean。请检查配置文件!");
- }
- return bean;
- }
- }
| |
开发人员只需要继承它就可以了,我们写个简单的示例action,如下:
- /**
- * 带ajax提交xml数据的action类模版
- *
- * @author 陈志平 chenzp
- *
- * @说明: web 应用基础平台
- * @date aug 1, 2006 10:52:13 am
- * @版权所有: all right reserved 2006-2008
- */
- public class useraction extends basedispatchdocumentaction {
- /**
- * 这里 actionform 和 doc 参数必有一个为空,请聪明的你分析一下
- * @param mapping --转发的映射对象
- [color=blue]* @param actionform --仍然支持表单提交,此时doc == null
- * @param doc document对象,解析xml后的文档对象[/color]
- * @param request --请求
- * @param response --响应
- */
- public actionforward list(actionmapping mapping, actionform actionform, [color=red]document doc[/color],httpservletrequest request, httpservletresponse response) throws baseappexception {
- /**
- * 转发的名称 useraction.search: 系统上下文 用于异常处理
- */
- expdisplaydetails.set(new exceptiondisplaydto(null, "useraction.search"));
- /**
- * 处理业务逻辑部分:
- *
- * 获取各种类型的参数 requestutil.getstrparameter(request,"parametername");
- *
- * 调用父类的 getspringbean("serviceid")方法获取spring的配置bean
- *
- */
- usermanager usermanager = (logmanager) getspringbean("usermanager");
- //返回xml对象到前台
- renderxml(response, usermanager.findusersbydoc(doc));
- return null;
- }
至此,我们成功实现了ajax--struts--spring的无缝结合,下次介绍spring的开发应用。欢迎大家拍砖!
闽公网安备 35060202000074号