您所在的位置:开发 > Web > Javascript > Ajax、Struts、Spring的无缝结合

Ajax、Struts、Spring的无缝结合

2006-12-27 09:41 简单就好 中华部落阁 字号:T | T
一键收藏,随时查看,分享好友!

本文通过作者亲历的医药信息系统的设计开发,架构设计来介绍Ajax、Struts、Spring间的协作。

AD:

去年初,正好负责一个医药信息系统的设计开发,架构设计时,采用Struts+JDBC(自定义采用适配器模式封装了HashMap动态VO实现的持久层)。后来ajax热潮兴起,正好系统中有很多地方需要和服务器端交互数据,如采购销售系统中的订单头/订单明细等主从表结构的维护。

数据交互过程,我们考虑采用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格式的字符串
* @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),并且完善了转发方式。如:

1.DispatchAction

以一个Controller响应一组动作绝对是Controller界的真理,Struts的DispatchAction同样可以做到这点。

parameter="method" validate="false">
    


其中parameter="method" 设置了用来指定响应方法名的url参数名为method,即/admin/user.do?method=list 将调用UserAction的public ActionForward list(....) 函数。  
 
public ActionForward unspecified(....) 函数可以指定不带method方法时的默认方法。
 
但是这样需要在url后多传递参数method=list ;并且action节点配置中的parameter="method"也没有被充分利用,反而觉得是累赘!
 
因此我们直接在BaseDispatchAction中增加xml字符串解析,并充分利用action节点配置中的parameter="targetMethod" ,使得转发的时候,action能够直接转发到子类的相应方法中,减少了url参数传递,增强了配置信息可读性,方便团队开发。  
 
同样以上述为例,扩展后的配置方式如下:

parameter="list" validate="false">
    
  

其中parameter="list" 设置了用来指定响应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

expDisplayDetails = new ThreadLocal();
        
        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 methods = new HashMap();
        /**
         * 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 ActionForward instance describing where and how
         * control should be forwarded, or null 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 execute
         * 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 --转发的映射对象
          * @param actionForm --仍然支持表单提交,此时doc == null
          * @param doc document对象,解析xml后的文档对象

          * @param request --请求
          * @param response --响应
          */
        public ActionForward list(ActionMapping mapping, ActionForm actionForm,

Document doc,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的无缝结合。欢迎大家拍砖!

来源链接:http://java.mblogger.cn/zhipingch/ 

(责任编辑 火凤凰 sunsj@51cto.com  TEL:(010)68476636-8007)




分享到:

  1. 程序员面试失败的几种原因,你中枪了吗?
  2. Java未来也许不再是电商的首选开发语言!

热点职位

更多>>

热点专题

更多>>

读书

精通ASP+XML+CSS网络开发混合编程
《精通ASP+XML+CSS网络开发混合编程》介绍当前网络开发的主流平台与技术之一的ASP+CSS+XML的知识与应用,全书各知识点均配以实例

51CTO旗下网站

领先的IT技术网站 51CTO 领先的中文存储媒体 WatchStor 中国首个CIO网站 CIOage 中国首家数字医疗网站 HC3i 51CTO学院