ajax、Struts、spring的无缝结合

网络整理 - 07-26
查询时:持久层根据实际需要返回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同样可以做到这点。

  

  < action path="/admin/user" name="userForm" scope="request" parameter="method" validate="false">

  < forward name="list" path="/admin/userList.jsp"/>

  < /action>

  

  其中parameter="method" 设置了用来指定响应方法名的url参数名为method,即/admin/user.do?method=list 将调用UserAction的public ActionForward list(....) 函数。

  public ActionForward unspecified(....) 函数可以指定不带method方法时的默认方法。[/quote]

  但是这样需要在url后多传递参数[color=red]method=list [/color];并且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;

  }