Archive for the 'struts' Category

You are currently browsing the archives of Enabling Technology .

一个Struts实现分页,增删改查,Tiles,国际化的源代码

 这个DEMO供大家一起探讨学习Struts,因为工作太累,没精力给大家解释实现原理。如果看不懂,没关系。只是说明JSP基础还没有到火候,不要心急,回去强化下JSP+Servlet,基础扎实了,自然能够看懂我写的代码。这个DEMO借鉴了网上很多前人的经验,在此一并谢谢。
web.xml文件:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE web-app PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN” “http://java.sun.com/dtd/web-app_2_3.dtd“>
<web-app>
  <display-name>BookShopMod</display-name>
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <init-param>
      <param-name>application</param-name>
      <param-value>ApplicationResources</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.htm</welcome-file>
  </welcome-file-list>
  <taglib>
    <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-html.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-template.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
  </taglib>
  <taglib>
    <taglib-uri>/WEB-INF/camel-define.tld</taglib-uri>
    <taglib-location>/WEB-INF/camel-define.tld</taglib-location>
  </taglib>
</web-app>
Struts-config.xml文件:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE struts-config PUBLIC “-//Apache Software Foundation//DTD Struts Configuration 1.1//EN” “http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd“>
<struts-config>
  <form-beans>
    <form-bean name=”bookForm” type=”com.bookshop.form.BookForm”/>
    <form-bean name=”operatorForm” type=”com.bookshop.form.OperatorForm”/>
    <form-bean name=”findRecordForm” type=”com.bookshop.form.FindRecordForm”/>
  </form-beans>
  <global-forwards>
    <forward name=”index” path=”/index.jsp”/>
    <forward name=”browser” path=”/show.jsp”/>
    <forward name=”global_error” path=”/error.jsp”/>
  </global-forwards>
  <action-mappings>
    <action input=”/show.jsp” name=”bookForm” parameter=”operator” path=”/operatorAction” scope=”session” type=”com.bookshop.action.OperatorAction” validate=”false”>
      <forward name=”operatorok” path=”/success.jsp” redirect=”true”/>
      <forward name=”showFirstPage” path=”/operatorAction.do?operator=showFirstPage”/>
      <forward name=”showPreviousPage” path=”/operatorAction.do?operator=showPreviousPage”/>
      <forward name=”showNextPage” path=”/operatorAction.do?operator=showNextPage”/>
      <forward name=”showLastPage” path=”/operatorAction.do?operator=showLastPage”/>
      <forward name=”showAddRecord” path=”/editrecord.jsp?operator=addRecord” redirect=”true”/>
      <forward name=”showModifyRecord” path=”/editrecord.jsp?operator=modifyRecord”/>
      <forward name=”showFindRecord” path=”/findrecord.jsp” redirect=”true”/>
    </action>
    <action input=”/findrecord.jsp” name=”findRecordForm” path=”/findRecordAction” scope=”session” type=”com.bookshop.action.FindRecordAction” validate=”false”/>
  </action-mappings>
  <plug-in className=”org.apache.struts.tiles.TilesPlugin”>
    <set-property property=”definitions-config” value=”/WEB-INF/tiles-defs.xml”/>
  </plug-in>
</struts-config>
tiles-defs文件:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE tiles-definitions PUBLIC “-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN” “http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd“>
<tiles-definitions>
  <definition name=”base-definition” path=”layout.jsp”>
    <put name=”head” value=”head.jsp” />
    <put name=”left” value=”left.jsp” />
    <put name=”body” />
    <put name=”foot” value=”foot.jsp” />
  </definition>
  <definition extends=”base-definition” name=”index-definition”>
    <put name=”body” value=”index_body.jsp” />
  </definition>
  <definition extends=”base-definition” name=”show-definition”>
    <put name=”body” value=”show_body.jsp” />
  </definition>
  <definition extends=”base-definition” name=”edit-definition”>
    <put name=”body” value=”edit_body.jsp” />
  </definition>
  <definition extends=”base-definition” name=”find-definition”>
    <put name=”body” value=”find_body.jsp”/>
    </definition>
   <definition extends=”base-definition” name=”success-definition”>
    <put name=”body” value=”success_body.jsp” />
  </definition>
  <definition extends=”base-definition” name=”error-definition”>
    <put name=”body” value=”error_body.jsp”/>
    </definition>
</tiles-definitions>
camel-define文件:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE taglib PUBLIC “-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN” “http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd“>
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>camel</shortname>
<uri>http://jakarta.apache.org/struts/tags-bean</uri>
<tag>
<name>isLastPage</name>
<tagclass>com.bookshop.util.IsLastTag</tagclass>
<bodycontent>JSP</bodycontent>
<attribute>
<name>page</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
上面几个文件和struts-bean.tld,struts-html.tld,struts-tiles.tld,struts-logic.tld都一起位于WEB-INF的根目录下面。
以下是三个Action文件:
/*FindRecordAction.java*/
package com.bookshop.action;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForward;
import com.bookshop.form.FindRecordForm;
import org.apache.struts.action.Action;
import java.util.List;
import java.util.ArrayList;
import com.bookshop.model.Operator;
import com.bookshop.util.PageInfo;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;

public class FindRecordAction
    extends Action {
  public ActionForward execute(ActionMapping actionMapping,
                               ActionForm actionForm,
                               HttpServletRequest servletRequest,
                               HttpServletResponse servletResponse) {
    FindRecordForm findRecordForm = (FindRecordForm) actionForm;
    String key = findRecordForm.getFindByKey().trim();
    String value = findRecordForm.getFindByValue().trim();
    List list = new ArrayList();
    list = Operator.getRecords(key, value, 0);
    servletRequest.getSession().setAttribute(”books”, list);
    if (!list.isEmpty()) {
      servletRequest.getSession().setAttribute(”pageinfo”,
                                               new PageInfo(Operator.
          getRecordsNumber(), 1));
    }
    else {
      ActionErrors messages = new ActionErrors();
      messages.add(ActionErrors.GLOBAL_MESSAGE,
                   new ActionError(”findrecord.jsp.notfound”));
      servletRequest.getSession().setAttribute(”pageinfo”,
                                               new PageInfo(0, 1));
    }
    return actionMapping.findForward(”browser”);
  }
}
/*GenericAction.java*/
package com.bookshop.action;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForward;
import org.apache.struts.actions.DispatchAction;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;

public class GenericAction
    extends DispatchAction {
  /*
     public ActionForward execute(ActionMapping actionMapping,
                               ActionForm actionForm,
                               HttpServletRequest servletRequest,
                               HttpServletResponse servletResponse) {
    throw new java.lang.UnsupportedOperationException(
        “Method $execute() not yet implemented.”);
     }
   */
  public void saveGlobalErrors(HttpServletRequest httpServletRequest,
                               String errorKey) {
    ActionErrors errors = new ActionErrors();
    errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(errorKey));
    if (errors != null) {
      saveErrors(httpServletRequest, errors);
    }
  }

  public ActionForward getIndexForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”index”);
  }

  public ActionForward getBrowserForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”browser”);
  }

  public ActionForward showDeleteForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”showDelete”);
  }

  public ActionForward getOperatorOkForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”operatorok”);
  }

  public ActionForward getErrorForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”global_error”);
  }

  public ActionForward getShowAddForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”showAddRecord”);
  }

  public ActionForward getShowModifyForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”showModifyRecord”);
  }

  public ActionForward getShowDeleteForward(ActionMapping actionMapping) {
    return actionMapping.findForward(”showDeleteRecord”);
  }

  public ActionForward getShowFindForward(ActionMapping actionMapping) {
      return actionMapping.findForward(”showFindRecord”);
  }
}
/*OperatorAction.java*/
package com.bookshop.action;

import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForward;
import com.bookshop.form.OperatorForm;
import org.apache.struts.action.Action;
import java.util.List;
import org.apache.struts.Globals;
import com.bookshop.util.DBUtil;
import com.bookshop.util.ApplicationUtil;
import com.bookshop.model.Operator;
import java.util.ArrayList;
import com.bookshop.util.PageInfo;
import org.apache.struts.actions.DispatchAction;
import java.util.Map;
import java.util.HashMap;
import com.bookshop.form.BookForm;
import java.util.Locale;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import com.bookshop.util.BookBean;

public class OperatorAction
    extends GenericAction {
    /*
       public ActionForward execute(ActionMapping actionMapping,
                                 ActionForm actionForm,
                                 HttpServletRequest servletRequest,
                                 HttpServletResponse servletResponse) {
      throw new java.lang.UnsupportedOperationException(
          “Method $execute() not yet implemented.”);
       }
     */

  //转换为中文页面
  public ActionForward ChangeCH(ActionMapping actionMapping,
                                ActionForm actionForm,
                                HttpServletRequest servletRequest,
                                HttpServletResponse servletResponse) {
    servletRequest.getSession().setAttribute(Globals.LOCALE_KEY, Locale.CHINA);
    return this.getIndexForward(actionMapping);
  }

  //转换为英文页面
  public ActionForward ChangeEN(ActionMapping actionMapping,
                                ActionForm actionForm,
                                HttpServletRequest servletRequest,
                                HttpServletResponse servletResponse) {
    servletRequest.getSession().setAttribute(Globals.LOCALE_KEY, Locale.ENGLISH);
    return this.getIndexForward(actionMapping);
  }

  //链接到首页记录
  public ActionForward showFirstPage(ActionMapping actionMapping,
                                     ActionForm actionForm,
                                     HttpServletRequest httpServletRequest,
                                     HttpServletResponse httpServletResponse) {
    List list = new ArrayList();
    list = Operator.getRecords(0);
    httpServletRequest.getSession().setAttribute(”books”, list);
    httpServletRequest.getSession().setAttribute(”pageinfo”,
                                                 new PageInfo(Operator.
        getRecordsNumber(), 1));
    return this.getBrowserForward(actionMapping);
  }

  //链接到上一页记录
  public ActionForward showPreviousPage(ActionMapping actionMapping,
                                        ActionForm actionForm,
                                        HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse) {
    List list = new ArrayList();
    PageInfo pageInfo = (PageInfo) httpServletRequest.getSession().getAttribute(
        “pageinfo”);
    list = Operator.getRecords( (pageInfo.getPreviousPageNumber() - 1) *
                               ApplicationUtil.recordPerPage);
    httpServletRequest.getSession().setAttribute(”books”, list);
    httpServletRequest.getSession().setAttribute(”pageinfo”,
                                                 new PageInfo(Operator.
        getRecordsNumber(), pageInfo.getPreviousPageNumber()));
    return this.getBrowserForward(actionMapping);
  }

  //链接到下一页记录
  public ActionForward showNextPage(ActionMapping actionMapping,
                                    ActionForm actionForm,
                                    HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse) {
    List list = new ArrayList();
    PageInfo pageInfo = (PageInfo) httpServletRequest.getSession().getAttribute(
        “pageinfo”);
    list = Operator.getRecords(pageInfo.getCurrentlyPage() *
                               ApplicationUtil.recordPerPage);
    httpServletRequest.getSession().setAttribute(”books”, list);
    httpServletRequest.getSession().setAttribute(”pageinfo”,
                                                 new PageInfo(Operator.
        getRecordsNumber(), pageInfo.getNextPageNumber()));
    return this.getBrowserForward(actionMapping);
  }

  //链接到末页记录
  public ActionForward showLastPage(ActionMapping actionMapping,
                                    ActionForm actionForm,
                                    HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse) {
    List list = new ArrayList();
    PageInfo pageInfo = (PageInfo) httpServletRequest.getSession().getAttribute(
        “pageinfo”);
    list = Operator.getRecords( (pageInfo.getPageCountNumber() - 1) *
                               ApplicationUtil.recordPerPage);
    httpServletRequest.getSession().setAttribute(”books”, list);
    httpServletRequest.getSession().setAttribute(”pageinfo”,
                                                 new PageInfo(Operator.
        getRecordsNumber(), pageInfo.getLastPageNumber()));
    return this.getBrowserForward(actionMapping);
  }

  //取消操作的转向
  public ActionForward cancel(ActionMapping actionMapping,
                              ActionForm actionForm,
                              HttpServletRequest httpServletRequest,
                              HttpServletResponse httpServletResponse) {
    if (isCancelled(httpServletRequest)) {
      return this.getOperatorOkForward(actionMapping);
    }
    return null;
  }

  //查看所有记录
  public ActionForward browser(ActionMapping actionMapping,
                               ActionForm actionForm,
                               HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse) {
    return this.showFirstPage(actionMapping, actionForm, httpServletRequest,
                              httpServletResponse);
  }

//执行添加记录
  public ActionForward addRecord(ActionMapping actionMapping,
                                 ActionForm actionForm,
                                 HttpServletRequest httpServletRequest,
                                 HttpServletResponse httpServletResponse) {
    BookForm bookForm = (BookForm) actionForm;
    if (Operator.addRecord(bookForm.loadBook()) >= 1) {
      return this.getOperatorOkForward(actionMapping);
    }
    else {
      this.saveGlobalErrors(httpServletRequest, “editrecord.jsp.adderror”);
      return this.getErrorForward(actionMapping);
    }
  }

//提交更新操作
  public ActionForward SubmitRecord(ActionMapping actionMapping,
                                    ActionForm actionForm,
                                    HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse) {
    String str = (String) httpServletRequest.getSession().getAttribute(”method”);
    if (str.equals(”addRecord”)) {
      return addRecord(actionMapping, actionForm, httpServletRequest,
                       httpServletResponse);
    }
    if (str.equals(”modifyRecord”)) {
      return modifyRecord(actionMapping, actionForm, httpServletRequest,
                          httpServletResponse);
    }
    else {
      this.saveGlobalErrors(httpServletRequest, “edit.body.error”);
      return this.getErrorForward(actionMapping);
    }
  }

//执行修改操作
  public ActionForward modifyRecord(ActionMapping actionMapping,
                                    ActionForm actionForm,
                                    HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse) {
    BookForm bookForm = (BookForm) actionForm;
    if (Operator.modifyRecord(bookForm.loadBook()) != -1) {
      return this.getOperatorOkForward(actionMapping);
    }
    else {
      this.saveGlobalErrors(httpServletRequest, “editrecord.jsp.modifyerror”);
      return this.getErrorForward(actionMapping);
    }
  }
//跳转到添加记录编辑页面
  public ActionForward showAdd(ActionMapping actionMapping,
                               ActionForm actionForm,
                               HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse) {
    httpServletRequest.getSession().setAttribute(”bookBean”, new BookForm());
    httpServletRequest.getSession().setAttribute(”method”,
                                                 new String(”addRecord”));
    return this.getShowAddForward(actionMapping);
  }

//跳转到修改记录编辑页面
  public ActionForward showModify(ActionMapping actionMapping,
                                  ActionForm actionForm,
                                  HttpServletRequest httpServletRequest,
                                  HttpServletResponse httpServletResponse) {
    BookBean book = new BookBean();
    String str = httpServletRequest.getParameter(”bookid”).toString();
    book = Operator.getRecord(str);
    httpServletRequest.getSession().setAttribute(”bookBean”,
                                                 new BookForm(book.getBookId(),
        book.getBookName(), book.getAuthor(), book.getPublish(), book.getPrice()));
    httpServletRequest.getSession().setAttribute(”method”,
                                                 new String(”modifyRecord”));

    return this.getShowModifyForward(actionMapping);
  }

//删除记录
  public ActionForward showDelete(ActionMapping actionMapping,
                                  ActionForm actionForm,
                                  HttpServletRequest httpServletRequest,
                                  HttpServletResponse httpServletResponse) {
    String str = httpServletRequest.getParameter(”bookid”).toString();
    if (Operator.deleteRecord(str) != -1) {
      return this.getOperatorOkForward(actionMapping);
    }
    else {
      this.saveGlobalErrors(httpServletRequest, “edit.body.error”);
      return this.getErrorForward(actionMapping);
    }
  }

  public ActionForward showFind(ActionMapping actionMapping,
                                ActionForm actionForm,
                                HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse) {
    //传递参数
    httpServletRequest.getSession().setAttribute(”bookBean”, new BookForm());
    httpServletRequest.getSession().setAttribute(”method”,
                                                 new String(”findRecord”));
    return this.getShowFindForward(actionMapping);
  }
}
以下是三个ActionForm文件:
package com.bookshop.form;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.HashMap;

public class BookForm
    extends ActionForm {
  private String author;
  private String bookId;
  private String bookName;
  private String price;
  private String publish;
  private String beanId;

  public BookForm() {
    this.bookId = “”;
    this.bookName = “”;
    this.author = “”;
    this.publish = “”;
    this.price = “”;
    this.beanId = “”;
  }

  public BookForm(String id, String name, String author, String publish,
                  String price) {
    this.bookId = id;
    this.bookName = name;
    this.author = author;
    this.publish = publish;
    this.price = price;
    this.beanId = id;
  }

  public String getAuthor() {
    return author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public void setPublish(String publish) {
    this.publish = publish;
  }

  public void setPrice(String price) {
    this.price = price;
  }

  public void setBookName(String bookName) {
    this.bookName = bookName;
  }

  public void setBookId(String bookId) {
    this.bookId = bookId;
  }

  public String getBookId() {
    return bookId;
  }

  public String getBookName() {
    return bookName;
  }

  public String getPrice() {
    return price;
  }

  public String getPublish() {
    return publish;
  }

  public String getBeanId() {
    return this.beanId;
  }

  public void setBeanId(String beanId) {
    this.beanId = beanId;
  }

  public Map loadBook() {
    Map record = new HashMap();
    record.put(”column1″, this.getBookId().trim());
    record.put(”column2″, this.getBookName().trim());
    record.put(”column3″, this.getAuthor().trim());
    record.put(”column4″, this.getPublish().trim());
    record.put(”column5″, this.getPrice().trim());
    return record;
  }

  public ActionErrors validate(ActionMapping actionMapping,
                               HttpServletRequest httpServletRequest) {
    ActionErrors errors = new ActionErrors();
    if (this.bookId == null || this.bookId.equals(”") ||
        this.bookId.length() < 1) {
      errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(”book.bookid.error”));
    }
    if (this.bookName == null || this.bookName.equals(”") ||
        this.bookName.length() < 1) {
      errors.add(ActionErrors.GLOBAL_ERROR,
                 new ActionError(”book.bookname.error”));
    }
    if (this.author == null || this.author.equals(”") ||
        this.author.length() < 1) {
      errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(”book.author.error”));
    }
    if (this.publish == null || this.publish.equals(”") ||
        this.publish.length() < 1) {
      errors.add(ActionErrors.GLOBAL_ERROR,
                 new ActionError(”book.publish.error”));
    }
    // if ( (Float.isNaN(this.price)) && (this.price < 0)) {
    if ( (Float.isNaN(Float.parseFloat(this.price))) &&
        (Float.parseFloat(this.price) < 0)) {
      errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(”book.price.error”));
    }
    return errors;
  }
}
/**/
package com.bookshop.form;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;

public class FindRecordForm
    extends ActionForm {
  private String findByKey;
  private String findByValue;
  public String getFindByKey() {
    return findByKey;
  }

  public void setFindByKey(String findByKey) {
    this.findByKey = findByKey;
  }

  public void setFindByValue(String findByValue) {
    this.findByValue = findByValue;
  }

  public String getFindByValue() {
    return findByValue;
  }

  public ActionErrors validate(ActionMapping actionMapping,
                               HttpServletRequest httpServletRequest) {
    /** @todo: finish this method, this is just the skeleton.*/
    ActionErrors errors = null;
    if (this.findByKey.equals(”") || this.findByValue.equals(”")) {
      errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,
                 new ActionError(”find.jsp.error”));
    }
    return errors;
  }

  public void reset(ActionMapping actionMapping,
                    HttpServletRequest servletRequest) {
  }
}
/**/
package com.bookshop.form;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;

public class OperatorForm
    extends ActionForm {
  private String operator;
  public String getOperator() {
    return operator;
  }

  public void setOperator(String operator) {
    this.operator = operator;
  }

  public ActionErrors validate(ActionMapping actionMapping,
                               HttpServletRequest httpServletRequest) {
    ActionErrors errors = new ActionErrors();
    if (httpServletRequest.getParameter(”operator”) != null) {
      String lang = httpServletRequest.getParameter(”operator”);
      /* if ( (lang.length() < 6) || (lang.length() > 6)) {
         errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(”index.jsp.operator.error”));
       }
       */
    }
    else {
      errors.add(ActionErrors.GLOBAL_ERROR,
                 new ActionError(”index.jsp.operator.null”));
    }
    return errors;

  }

  public void reset(ActionMapping actionMapping,
                    HttpServletRequest servletRequest) {
  }
}
以下是业务类和数据库访问类:
package com.bookshop.model;

import java.util.Map;
import java.util.List;
import com.bookshop.util.*;

public class Operator {

  private static int recordPerPage = ApplicationUtil.recordPerPage;
  //动态SQL
  private static String sqlNumber = “”;

  //留出接口设置每页显示多少条记录
  public static void setRecordPerPage(int number) {
    recordPerPage = number;
  }

  public Operator() {
  }

  //获得所有记录集(只查询一页记录)
  public static List getRecords(int startIndex) {
    String sql = “select * from booktab limit ?,?”;
    sqlNumber = “select count(*) from booktab”;
    return DBUtil.executeQuery(sql, startIndex, recordPerPage);
  }

  //按条件查找记录集(只查询一页记录)
  public static List getRecords(String key, String value, int startIndex) {

    String sql = “select * from booktab where ” + key + “=’” + value +
        “‘ limit ?,?”;
    sqlNumber = “select count(*) from booktab where ” + key + “=’” + value +
        “‘”;
    return DBUtil.executeQuery(sql, startIndex, recordPerPage);
  }

  //查询单条记录 用于修改
  public static BookBean getRecord(String value) {
    String sql = “select * from booktab where bookid=’” + value + “‘”;
    BookBean book = new BookBean();
    book = DBUtil.execQuery(sql);
    return book;
  }

  //添加一条新记录
  public static int addRecord(Map newRecord) {
    String sql =
        “insert into booktab(bookname,author,publish,price,bookid)values(?,?,?,?,?)”;
    return DBUtil.execUpdate(sql, newRecord);
  }

  //修改指定的记录
  public static int modifyRecord(Map newRecord) {
    String sql =
        “update booktab set bookname=?,author=?,publish=?,price=? where bookid=?”;
    return DBUtil.execUpdate(sql, newRecord);
  }

  //删除指定的记录
  public static int deleteRecord(String value) {
    String sql =
        “delete from booktab where bookid=’” + value + “‘”;
    return DBUtil.execUpdate(sql);
  }

  //获得表中所有记录的条数
  public static int getRecordsNumber() {
    return DBUtil.executeQuery(sqlNumber);
  }

  /*
    public static void main(String[] args) {
      Operator operator = new Operator();
    }
   */
}
/**/
package com.bookshop.util;

import javax.servlet.http.HttpServletRequest;

public class ApplicationUtil {
  public ApplicationUtil() {
  }

  public static final String driver = “org.gjt.mm.mysql.Driver”;
  public static final String url =”jdbc:mysql://localhost/bookshop”;
  public static final String user = “root”;
  public static final String password = “”;
  public static final int recordPerPage = 5;

  public static String toGBK(String s) {
    try {
      return new String(s.getBytes(”ISO-8859-1″), “gb2312″);
    }
    catch (Exception ex) {
      return “”;
    }
  }

  public static String getSelfURL(HttpServletRequest req) {
    String s1 = req.getRequestURI();
    String s2 = req.getQueryString();
    if (s2 != null) {
      s1 = s1 + “?” + s2;
    }
    return s1;
  }

  public static void setParam(String param, String value, java.util.HashMap map) {
    String[] p = param.split(”;”);
    String[] v = value.split(”;”);
    for (int i = 0; i < p.length; i++) {
      map.put(p[i], v[i]);
    }
  }

}
/**/
package com.bookshop.util;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

public class IsLastTag
    extends TagSupport {
  private String page = “”;

  public IsLastTag() {
  }

  public void setPage(String page) {
    this.page = page;
  }

  public String getPage() {
    return this.page;
  }

  public int doStartTag() throws JspException {
    if (this.page != null) {
      //从session里面取出来的是PageInfo对象的引用
      PageInfo pageBean = new PageInfo();
      pageBean = (PageInfo) (pageContext.getSession().getAttribute(this.
          page));
      //只要该PageInfo对象的总页数等于当前页数就不处理主体部分
      if (pageBean.getPageCountNumber() <= pageBean.getCurrentlyPage()) {
        return this.SKIP_BODY;
      }
      else {
        return this.EVAL_PAGE;//否则继续处理主体部分
      }
    }
    else {
      return this.SKIP_BODY;
    }
  }
}
/**/
package com.bookshop.util;

import java.util.List;
import java.util.ArrayList;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class DBUtil {

  public DBUtil() {
  }

  private static String driver = ApplicationUtil.driver;
  private static String url = ApplicationUtil.url;
  private static String user = ApplicationUtil.user;
  private static String password = ApplicationUtil.password;
  private static List list = null;
  private static Connection con = null;
  private static Statement sta = null;
  private static PreparedStatement psta = null;
  private static ResultSet res = null;

//获得满足查询条件的记录行数
  public static int executeQuery(String sql) {
    int countNum = 0;
    try {
      execute(sql);
      while (res.next()) {
        countNum = res.getInt(1);
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      close();
      return countNum;
    }
  }

//删除记录
  public static int execUpdate(String sql) {
    int i = -1;
    try {
      getStatement();
      i = sta.executeUpdate(sql);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      close();
      return i;
    }
  }

//添加新记录
  public static int execUpdate(String sql, Map newRecord) {
    int i = -1;
    try {
      getPreparedStatement(sql);
      if (newRecord != null && !newRecord.isEmpty()) {
        psta.setString(1, (String) newRecord.get(”column2″));
        psta.setString(2, (String) newRecord.get(”column3″));
        psta.setString(3, (String) newRecord.get(”column4″));
        psta.setString(4, (String) newRecord.get(”column5″));
        psta.setString(5, (String) newRecord.get(”column1″));
      }
      i = psta.executeUpdate();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      close();
      return i;
    }
  }
//查询单个记录(用于修改)
  public static BookBean execQuery(String sql) {
    BookBean book = null;
    try {
      execute(sql);
      while (res.next()) {
        book = new BookBean(ApplicationUtil.toGBK(res.getString(1)),
                            ApplicationUtil.toGBK(res.getString(2)),
                            ApplicationUtil.toGBK(res.getString(3)),
                            ApplicationUtil.toGBK(res.getString(4)),
                            ApplicationUtil.toGBK(res.getString(5))
            );
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      close();
      return book;
    }
  }
//查询记录(只查询指定页要显示的记录)
  public static List executeQuery(String sql, int startIndex, int count) {
    try {
      list = new ArrayList();
      getResultSet(sql, startIndex, count);
      while (res.next()) {
        list.add(new BookBean(ApplicationUtil.toGBK(res.getString(1)),
                              ApplicationUtil.toGBK(res.getString(2)),
                              ApplicationUtil.toGBK(res.getString(3)),
                              ApplicationUtil.toGBK(res.getString(4)),
                              ApplicationUtil.toGBK(res.getString(5))
                 ));
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      close();
      return list;
    }
  }

  private static void getConnection() throws Exception {
    Class.forName(driver);
    con = DriverManager.getConnection(url, user, password);
    //con.setAutoCommit(false);
  }

  private static void getPreparedStatement(String sql) throws Exception {
    getConnection();
    psta = con.prepareStatement(sql);
  }

  private static void execute(String sql) throws Exception {
    getStatement();
    res = sta.executeQuery(sql);
  }

  private static void getStatement() throws Exception {
    getConnection();
    sta = con.createStatement();
  }

  private static void getResultSet(String sql, int startIndex, int count) throws
      Exception {
    getPreparedStatement(sql);
    psta.setInt(1, startIndex);
    psta.setInt(2, count);
    res = psta.executeQuery();
  }

//释放资源
  private static void close() {
    try {
      /*
             if(con!=null){
         con.commit();
       }
       */
      if (res != null) {
        res.close();
      }
      if (psta != null) {
        psta.close();
      }
      if (sta != null) {
        sta.close();
      }
      if (con != null) {
        con.close();
      }
    }
    catch (SQLException e) {
      e.printStackTrace();
    }
  }
}
/**/
package com.bookshop.util;

import java.io.Serializable;

public class BookBean
    implements Serializable {
  private String author = “”;
  private String bookId = “”;
  private String bookName = “”;
  private String price = “”;
  private String publish = “”;

  public BookBean() {
    this.bookId = “”;
    this.bookName = “”;
    this.author = “”;
    this.publish = “”;
    this.price = “”;
  }

  public BookBean(String bookId, String bookName, String author, String publish,
                  String price) {
    this.author = author;
    this.bookId = bookId;
    this.bookName = bookName;
    this.publish = publish;
    this.price = price;
  }

  public String getAuthor() {
    return author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public void setPublish(String publish) {
    this.publish = publish;
  }

  public void setPrice(String price) {
    this.price = price;
  }

  public void setBookName(String bookName) {
    this.bookName = bookName;
  }

  public void setBookId(String bookId) {
    this.bookId = bookId;
  }

  public String getBookId() {
    return bookId;
  }

  public String getBookName() {
    return bookName;
  }

  public String getPrice() {
    return price;
  }

  public String getPublish() {
    return publish;
  }
  /*
    public static void main(String[] args) {
      BookBean bookbean = new BookBean();
    }
   */
}
/**/
package com.bookshop.util;

import java.io.Serializable;

public class PageInfo
    implements Serializable {

  private int recordCountNumber = 0; //总记录数
  private int pageCountNumber = 0; //总页数
  private int recordPerPage = ApplicationUtil.recordPerPage; //每页记录数
  private int currentlyPage = 0; //当前页数
  private int previousPageNumber = 0; //当前页的前一页数
  private int nextPageNumber = 0; //当前页的后一页数

  public PageInfo() {
  }

//总记录数 当前页码
  public PageInfo(int recordCountNumber,
                  int currentPage) {
    this.recordCountNumber = recordCountNumber;
    if (this.recordCountNumber % this.recordPerPage == 0) {
      this.pageCountNumber = this.recordCountNumber / this.recordPerPage;
    }
    else {
      this.pageCountNumber = (this.recordCountNumber / this.recordPerPage) + 1;
    }
    this.currentlyPage = currentPage;
  }

  public int getRecordCountNumber() {
    return this.recordCountNumber;
  }

  public int getPageCountNumber() {
    return this.pageCountNumber;
  }

  public int getRecordPerPage() {
    return this.recordPerPage;
  }

  public int getCurrentlyPage() {
    return this.currentlyPage;
  }

  public int getLastPageNumber() {
    return this.pageCountNumber;
  }

  public int getPreviousPageNumber() {
    this.previousPageNumber=this.currentlyPage-1;
    return this.previousPageNumber;
  }

  public int getNextPageNumber() {
    this.nextPageNumber = this.currentlyPage + 1;
    return this.nextPageNumber;
  }
}
以下是所有的JSP页面因为使用了Tiles框架,所以好象页面显得很多:
head.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<font color=red><html:errors/></font><br/>
<html:link href=”operatorAction.do?operator=ChangeCH”>
  <bean:message key=”head.language.chinese”/>
</html:link>
<html:link href=”operatorAction.do?operator=ChangeEN”>
  <bean:message key=”head.language.english”/>
</html:link>

left.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<html:link href=”index.jsp”>
  <bean:message key=”left.link.index”/>
</html:link>
<br/>
<html:link href=”operatorAction.do?operator=browser”>
  <bean:message key=”left.link.show”/>
</html:link>
<br/>
<html:link href=”operatorAction.do?operator=showFind”>
  <bean:message key=”left.link.findbykey”/>
</html:link>
<br/>
<html:link href=”operatorAction.do?operator=showAdd”>
  <bean:message key=”left.link.addnew”/>
</html:link>
<br/>

foot.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@ taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean” %>
<center>
  <bean:message key=”foot.copyright”/>
</center>

layout.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<%@taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles”%>
<html:html locale=”true”>
<body bgcolor=”#ffffff”>
  <table border=”0″ width=”100%”>
    <tr>
      <td width=”100%” height=”91″ colspan=”2″>
        <tiles:insert attribute=”head”/>
      </td>
    </tr>
    <tr>
      <td width=”16%” height=”290″>
        <tiles:insert attribute=”left”/>
      </td>
      <td width=”84%” height=”290″>
        <tiles:insert attribute=”body”/>
      </td>
    </tr>
    <tr>
      <td width=”100%” height=”83″ colspan=”2″>
        <tiles:insert attribute=”foot”/>
      </td>
    </tr>
  </table>
</body>
</html:html>

index_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<center>
  <html:link href=”operatorAction.do?operator=browser”>
    <bean:message key=”index.body.nextshow”/>
  </html:link>
</center>

index.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”index-definition”/>

show_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<%@taglib uri=”/WEB-INF/struts-logic.tld” prefix=”logic”%>
<%@taglib uri=”/WEB-INF/camel-define.tld” prefix=”camel”%>
<html:form action=”operatorAction.do” method=”post”>
  <bean:message key=”show.body.totalrecords”/>
  <bean:write name=”pageinfo” property=”recordCountNumber” scope=”session”/>
  &nbsp;
  <bean:message key=”show.body.totalpages”/>
  <bean:write name=”pageinfo” property=”pageCountNumber” scope=”session”/>
  &nbsp;
  <bean:message key=”show.body.currentlypage”/>
  <bean:write name=”pageinfo” property=”currentlyPage” scope=”session”/>
  &nbsp;
  <logic:greaterThan name=”pageinfo” property=”currentlyPage” value=”1″>
    <html:link href=”operatorAction.do?operator=showFirstPage”>
      <bean:message key=”show.body.first”/>
    </html:link>
    &nbsp;
    <html:link href=”operatorAction.do?operator=showPreviousPage”>
      <bean:message key=”show.body.previous”/>
    </html:link>
    &nbsp;
  </logic:greaterThan>
  <camel:isLastPage page=”pageinfo”>
    <html:link href=”operatorAction.do?operator=showNextPage”>
      <bean:message key=”show.body.next”/>
    </html:link>
    &nbsp;
    <html:link href=”operatorAction.do?operator=showLastPage”>
      <bean:message key=”show.body.last”/>
    </html:link>
  </camel:isLastPage>
  <br/>
  <table align=”left” border=”1″ width=”70%”>
    <tr bgcolor=”#COCOCO”>
      <td align=”center”>
        <bean:message key=”book.id”/>
      </td>
      <td align=”center”>
        <bean:message key=”book.name”/>
      </td>
      <td align=”center”>
        <bean:message key=”book.author”/>
      </td>
      <td align=”center”>
        <bean:message key=”book.publish”/>
      </td>
      <td align=”center”>
        <bean:message key=”book.price”/>
      </td>
      <td align=”center”>
        <bean:message key=”book.operator”/>
      </td>
    </tr>
    <logic:iterate id=”book” name=”books” scope=”session”>
      <tr>
        <td width=”10%”>
          <bean:write name=”book” property=”bookId”/>
        </td>
        <td width=”25%”>
          <bean:write name=”book” property=”bookName”/>
        </td>
        <td width=”10%”>
          <bean:write name=”book” property=”author”/>
        </td>
        <td width=”25%”>
          <bean:write name=”book” property=”publish”/>
        </td>
        <td width=”10″>
          <bean:write name=”book” property=”price”/>
        </td>
        <td width=”25%”>
          <a href=”operatorAction.do?operator=showModify&bookid=<bean:write name=”book” property=”bookId”/>”>
            <bean:message key=”link.modify”/>
          </a>
          &nbsp;&nbsp;
          <a href=”operatorAction.do?operator=showDelete&bookid=<bean:write name=”book” property=”bookId”/>”>
            <bean:message key=”link.delete”/>
          </a>
        </td>
        <bean:define id=”bookid” name=”book” property=”bookId”/>
        </tr>
    </logic:iterate>
  </table>
</html:form>

show.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”show-definition”/>

edit_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<%@taglib uri=”/WEB-INF/struts-logic.tld” prefix=”logic”%>
<!–
  <script src=”/WEB-INF/js/check.js” type=”javascript”/>
  onsubmit=”return check(this)”
  <html:errors/>
–>
<center>
  <html:form action=”/operatorAction.do?operator=SubmitRecord” method=”post”>
    <logic:present name=”bookBean”>
      <table align=”center” border=”0″>
        <tr>
          <td>
            <bean:message key=”book.id”/>
          </td>
          <td>
            <html:text name=”bookBean” property=”bookId”/>
          </td>
        </tr>
        <tr>
          <td>
            <bean:message key=”book.name”/>
          </td>
          <td>
            <html:text name=”bookBean” property=”bookName”/>
          </td>
        </tr>
        <tr>
          <td>
            <bean:message key=”book.author”/>
          </td>
          <td>
            <html:text name=”bookBean” property=”author”/>
          </td>
        </tr>
        <tr>
          <td>
            <bean:message key=”book.publish”/>
          </td>
          <td>
            <html:text name=”bookBean” property=”publish”/>
          </td>
        </tr>
        <tr>
          <td>
            <bean:message key=”book.price”/>
          </td>
          <td>
            <html:text name=”bookBean” property=”price”/>
          </td>
        </tr>
        <tr>
          <td>
            <html:submit property=”submit”>
              <bean:message key=”button.submit”/>
            </html:submit>
          </td>
          <td>
            <html:reset>
              <bean:message key=”button.reset”/>
            </html:reset>
          </td>
        </tr>
      </table>
    </logic:present>
  </html:form>
</center>

editrecord.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”edit-definition”/>

find_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
<%@taglib uri=”/WEB-INF/struts-logic.tld” prefix=”logic”%>
<%@taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles”%>
<center>
  <html:form action=”findRecordAction.do” method=”post”>
    <html:select property=”findByKey”>
      <html:option value=”bookName”>
        <bean:message key=”find.jsp.findkey.bookname”/>
      </html:option>
      <html:option value=”author”>
        <bean:message key=”find.jsp.findkey.author”/>
      </html:option>
      <html:option value=”publish”>
        <bean:message key=”find.jsp.findkey.publish”/>
      </html:option>
    </html:select>
    <html:text property=”findByValue”/>
    <br/>
    <html:submit>
      <bean:message key=”find.jsp.submit”/>
    </html:submit>
  </html:form>
</center>

findrecord.jsp:
<%@ page contentType=”text/html; charset=GBK” %>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”find-definition”/>

success_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
  <table width=”50%” border=”0″ align=”center” cellpadding=”10″ cellspacing=”0″>
    <tr>
      <td bgcolor=”#A4A4A4″>
        <table width=”100%” border=”0″ cellpadding=”0″ cellspacing=”0″>
          <tr>
            <td bgcolor=”#FFFFFF”>
              <table width=”100%” border=”0″ cellpadding=”15″ cellspacing=”1″>
                <tr>
                  <td bgcolor=”#E1E1E1″>
                    <div align=”center”>
                      <strong>
                        <html:link href=”/operatorAction.do?operator=showFirstPage”>
                          <bean:message key=”success.jsp.operatorerror”/>
                          <bean:message key=”jsp.back”/>
                        </html:link>
                      </strong>
                    </div>
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>

success.jsp:
<%@ page contentType=”text/html; charset=GBK” %>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”success-definition”/>

error_body.jsp:
<%@page contentType=”text/html; charset=GBK”%>
<%@taglib uri=”/WEB-INF/struts-bean.tld” prefix=”bean”%>
<%@taglib uri=”/WEB-INF/struts-html.tld” prefix=”html”%>
  <table width=”50%” border=”0″ align=”center” cellpadding=”10″ cellspacing=”0″>
    <tr>
      <td bgcolor=”#A4A4A4″>
        <table width=”100%” border=”0″ cellpadding=”0″ cellspacing=”0″>
          <tr>
            <td bgcolor=”#FFFFFF”>
              <table width=”100%” border=”0″ cellpadding=”15″ cellspacing=”1″>
                <tr>
                  <td bgcolor=”#E1E1E1″>
                    <div align=”center”>
                      <strong>
                        <html:link href=”/operatorAction.do?operator=showFirstPage”>
                          <bean:message key=”error.jsp.operatorerror”/>
                          <bean:message key=”jsp.back”/>
                        </html:link>
                      </strong>
                    </div>
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>

error.jsp:
<%@ page contentType=”text/html; charset=GBK” %>
<%@ taglib uri=”/WEB-INF/struts-tiles.tld” prefix=”tiles” %>
<tiles:insert definition=”error-definition”/>

以下是消息资源文件,都位于WEB-INF/classes/目录下,
中文消息资源文件ApplicationResources.properties:
head.title.welcome=\u5934\u90e8!
head.language.chinese=\u4e2d\u6587
head.language.english=\u82f1\u6587

left.title.welcome=\u5de6\u8fb9\u83dc\u5355!
left.link.index=\u9996\u9875
left.link.show=\u6d4f\u89c8\u4e66\u7c4d
left.link.addnew=\u6dfb\u52a0\u4e66\u7c4d

foot.title.welcome=\u5e95\u90e8!
foot.copyright=\u7248\u6743\u6240\u6709 @2005.5 by \u5317\u5927\u9752\u9e1f

index.body.title=\u9996\u9875\u4e3b\u4f53\u90e8\u5206!
show.body.title=\u6d4f\u89c8\u9875\u4e3b\u4f53\u90e8\u5206!

index.body.nextshow=\u6d4f\u89c8

index.jsp.lang.error=<font color=red>\u8bed\u8a00\u53c2\u6570\u9519\u8bef</font>
index.jsp.language.null=<font color=red>\u8bed\u8a00\u53c2\u6570\u4e3a\u7a7a</font>
index.jsp.operator.error=<font color=red>\u64cd\u4f5c\u53c2\u6570\u9519\u8bef</font>
index.jsp.operator.null=<font color=red>\u64cd\u4f5c\u53c2\u6570\u4e3a\u7a7a</font>

link.delete=\u5220\u9664
link.modify=\u4fee\u6539

book.id=\u4e66\u53f7
book.name=\u4e66\u540d
book.author=\u4f5c\u8005
book.publish=\u51fa\u7248\u793e
book.price=\u5355\u4ef7
book.operator=\u64cd\u4f5c
button.submit=\u63d0\u4ea4
button.reset=\u91cd\u7f6e
button.cacel=\u53d6\u6d88

show.body.totalrecords=\u603b\u8bb0\u5f55\u6570:
show.body.totalpages=\u603b\u9875\u6570:
show.body.currentlypage=\u5f53\u524d\u9875\u7801:
show.body.first=\u6700\u524d\u9875
show.body.previous=\u4e0a\u4e00\u9875
show.body.next=\u4e0b\u4e00\u9875
show.body.last=\u6700\u540e\u9875

book.bookid.error=\u4e66\u53f7\u4e3a\u7a7a\u6216\u9519\u8bef!
book.bookname.error=\u4e66\u540d\u4e3a\u7a7a\u6216\u9519\u8bef!
book.author.error=\u4f5c\u8005\u4e3a\u7a7a\u6216\u9519\u8bef!
book.publish.error=\u51fa\u7248\u793e\u4e3a\u7a7a\u6216\u9519\u8bef!
book.price.error=\u5355\u4ef7\u4e3a\u7a7a\u6216\u9519\u8bef!
editrecord.jsp.adderror=\u6dfb\u52a0\u8bb0\u5f55\u672a\u6210\u529f!
editrecord.jsp.modifyerror=\u4fee\u6539\u8bb0\u5f55\u672a\u6210\u529f!
editrecord.jsp.deleteerror=\u5220\u9664\u8bb0\u5f55\u672a\u6210\u529f!
editrecord.body.title=\u7f16\u8f91\u9875\u9762\u4e3b\u4f53!

edit.body.error=\u975e\u6cd5\u64cd\u4f5c!

error.jsp.operatorerror=\u64cd\u4f5c\u9519\u8bef!

left.link.findbykey=\u81ea\u5b9a\u4e49\u67e5\u8be2
find.jsp.error=<font color=red>\u8bf7\u9009\u62e9\u548c\u586b\u5199\u67e5\u8be2\u53c2\u6570!</font>
find.jsp.submit=\u67e5\u627e
find.jsp.findkey.bookname=\u4e66\u540d\u79f0
find.jsp.findkey.author=\u4f5c\u8005
find.jsp.findkey.publish=\u51fa\u7248\u793e
success.jsp.operatorerror=\u64cd\u4f5c\u6210\u529f
jsp.back=\u8fd4\u56de\u4e3b\u9875\u9762
findrecord.jsp.notfound=\u672a\u67e5\u627e\u5230\u76f8\u5173\u8bb0\u5f55!

英文消息资源文件ApplicationResources_en.properties:
head.title.welcome=Head!
head.language.chinese=Chinese
head.language.english=English

left.title.welcome=Welcome Left!
left.link.index=Index
left.link.show=Browse Book
left.link.addnew=Add New Book

foot.title.welcome=Welcome foot!
foot.copyright=Copyright @2005.5 by Camel

index.body.title=Welcome index body!
show.body.title=Welcome browser body!

index.body.nextshow=SHOW

index.jsp.lang.error=<font color=red>Language Error</font>
index.jsp.language.null=<font color=red>Language Null</font>
index.jsp.operator.error=<font color=red>Operator Error</font>
index.jsp.operator.null=<font color=red>Operator Null</font>

link.delete=DELETE
link.modify=MODIFY

book.id=ID
book.name=BOOK NAME
book.author=AUTHOR
book.publish=PUBLISH
book.price=PRICE
book.operator=OPERATOR
button.submit=Submit
button.reset=Reset
button.cancel=Cancel

show.body.totalrecords=Total Records:
show.body.totalpages=Total Pages:
show.body.currentlypage=Currently Page:
show.body.first=First
show.body.previous=Previous
show.body.next=Next
show.body.last=Last

book.bookid.error=BookID is null or error!
book.bookname.error=BookName is null or error!
book.author.error=Author is null or error!
book.publish.error=Publish is null or error!
book.price.error=Price is null or not number!

editrecord.jsp.adderror=Add Record Error!
editrecord.jsp.modifyerror=Modify Record Error!
editrecord.jsp.deleteerror=Delete Record Error!
editrecord.body.title=Welcome Edit page body!

edit.body.error=Invalid Operator!

error.jsp.operatorerror=Operator error
jsp.back=Back main page
left.link.findbykey=Custom Find
find.jsp.error=<font color=red>Please select find key and input value!</font>
find.jsp.submit=Find
find.jsp.findkey.bookname=BookName
find.jsp.findkey.author=Author
find.jsp.findkey.publish=Publish
success.jsp.operatorerror=Operator success
jsp.back=Back main page
findrecord.jsp.notfound=It’s nothing to found!

我使用的是MySQL4.0数据库,驱动程序为:mm.mysql-2.0.14-bin.jar,数据脚本bookshop如下:
create database bookshop;
use bookshop;
create table booktab
(bookid   varchar(20) primary key not null,
 bookname varchar(50) not null default ”,
 author   varchar(50) not null default ”,
 publish  varchar(50) not null default ”,
 price    varchar(20) not null default 0.00);
测试记录集由大家去Insert20多条吧。

 工作中体会最深的是,对于应用软件项目来说,最重要的是客户的需求,使用什么技术不是最重要的,所以想和大家说一下,不要执迷于技术。对于一个软件项目,编写代码只占整个开发周期的20%,甚至更低。大部分的时间是在围绕客户需求做功能测试。能够写出上面的代码,对于一个程序员只能说只是达到了最基本的要求,对于一个程序员来说,最重要的是执着,耐心和锲而不舍。
 

Posted by micas on Jun 28th 2007 | Filed in struts | Comments (0)

struts中使用tiles组件

 1.在你的struts配置文件struts-config.xml中加入下面的配置:
 <plug-in className=”org.apache.struts.tiles.TilesPlugin” >
    <set-property property=”definitions-config” value=”/WEB-INF/tiles-def.xml” />
    <set-property property=”definitions-parser-validate” value=”true” />
 </plug-in>
2.生成tiles-def.xml文件:
<?xml version=”1.0″ encoding=”ISO-8859-1″ ?>

 <!DOCTYPE tiles-definitions PUBLIC
       “-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN”
       “http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd“>

<tiles-definitions>

 <definition name=”base-definition” path=”/layout.jsp”>
    <put name=”sidebar” value=”sidebar.jsp”/>
    <put name=”header” value=”header.jsp”/>
    <put name=”content” value=”"/>
    <put name=”footer” value=”footer.jsp”/>
 </definition>

 <definition name=”index-definition” extends=”base-definition”>
    <put name=”content” value=”indexContent.jsp”/>
 </definition>

</tiles-definitions>
3.生成layout.jsp布局文件:
<%@ page contentType=”text/html; charset=GBK” %>
<%@ taglib uri=”/tags/struts-tiles” prefix=”tiles”%> 
<html> 
<head> <title>布局设计</title> </head> 
<body > 
<table width=”100%” height=”100%”> 
<tr> 
  <td width=”150″ valign=”top” align=”left” bgcolor=”#CCFFCC”>
      <tiles:insert attribute=”sidebar”/> 
  </td> 
  <td valign=”top” height=”100%” width=”*”> 
    <table width=”100%” height=”100%”> 
       <tr> <td height=”15%”> <tiles:insert attribute=”header”/> </td> </tr>
       <tr> <td valign=”top” height=”*”> <tiles:insert attribute=”content”/> </td></tr>
      <tr> <td valign=”bottom” height=”15%”><tiles:insert attribute=”footer”/></td></tr>
    </table> 
  </td> 
</tr> 
</table> 
</body> </html> 

4.生成要使用的JSP文件sidebar.jsp,header.jsp,footer.jsp。

5.通过action-mappings配置你的tiles组件:
<action-mappings>
  <action path=”/index” type=”org.apache.struts.actions.ForwardAction” 
     parameter=”index-definition”>
  </action>
</action-mappings>
parameter参数的值,是你在tiles-def.xml文件里某个的define的name。

6.别忘了在web.xml中加入
<taglib>
    <taglib-uri>/tags/struts-tiles</taglib-uri>
    <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
  </taglib>

同时还要保证你使用的是struts 1.1版本。
现在就可以动手为你的项目加入tiles应用了。
完成以上步骤,完成Tomcate部署并启动,通过http://127.0.0.1:8080/strutsTiles/index.do
可以看到效果. 运行下面两个文件是同样的效果:

1、index1.jsp
<%@ page contentType=”text/html; charset=gb2312″ %>
<%@ taglib uri=”/tags/struts-tiles” prefix=”tiles” %>
使用逻辑名
<tiles:insert definition=”index-definition”/>

2、index.jsp

<%@ page contentType=”text/html; charset=gb2312″ %>
<%@ taglib uri=”/tags/struts-tiles” prefix=”tiles” %>
<tiles:insert page=”layout.jsp” flush=”true”>  
      <tiles:put name=”sidebar” value=”sidebar.jsp”/>
      <tiles:put name=”header”  value=”header.jsp”/>   
      <tiles:put name=”content” value=”indexContent.jsp”/>   
      <tiles:put name=”footer”  value=”footer.jsp”/>   
</tiles:insert> 

Posted by micas on Jun 28th 2007 | Filed in struts | Comments (0)

Struts常见异常信息和解决方法

以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的,如果你使用了多模块,或指定了不同的资源文件名称,这些名字要做相应的修改。

1、“No bean found under attribute key XXX”
在struts-config.xml里定义了一个ActionForm,但type属性指定的类不存在,type属性的值应该是Form类的全名。或者是,在Action的定义中,name或attribute属性指定的ActionForm不存在。

2、“Cannot find bean XXX in any scope”
在Action里一般会request.setAttribute()一些对象,然后在转向的jsp文件里(用tag或request.getAttribute()方法)得到这些对象并显示出来。这个异常是说jsp要得到一个对象,但前面的Action里并没有将对象设置到request(也可以是session、servletContext)里。
可能是名字错了,请检查jsp里的tag的一般是name属性,或getAttribute()方法的参数值;或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。
还有另外一个可能,纯粹是jsp文件的问题,例如<logic:iterate>会指定一个id值,然后在循环里<bean:write>使用这个值作为name的值,如果这两个值不同,也会出现此异常。(都是一个道理,request里没有对应的对象。)

3、“Missing message for key “XXX””
缺少所需的资源,检查ApplicationResources.properties文件里是否有jsp文件里需要的资源,例如:

<bean:message key=msg.name.prompt/>

这行代码会找msg.name.prompt资源,如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时,要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称,否则当然什么资源也找不到,这也是一个很容易犯的错误。

4、“No getter method for property XXX of bean teacher”
这条异常信息说得很明白,jsp里要取一个bean的属性出来,但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对:

<bean:write name=teacher property=cade filter=true/>
5、“Cannot find ActionMappings or ActionFormBeans collection”
待解决。

6、“Cannot retrieve mapping for action XXX”
在.jsp的<form>标签里指定action=’/XXX’,但这个Action并未在struts-config.xml里设置过。

7、HTTP Status 404 - /xxx/xxx.jsp
Forward的path属性指向的jsp页面不存在,请检查路径和模块,对于同一模块中的Action转向,path中不应包含模块名;模块间转向,记住使用contextRelative=”true”。

8、没有任何异常信息,显示空白页面
可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。

9、“The element type “XXX” must be terminated by the matching end-tag “XXX”.”
这个是struts-config.xml文件的格式错误,仔细检查它是否是良构的xml文件,关于xml文件的格式这里就不赘述了。

10、“Servlet.init() for servlet action threw exception”
一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息,其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下:

java.lang.NullPointerException
    at org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:
1003)
    at org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:
955)

为解决问题,先下载struts的源码包,然后在ActionServlet.java的第1003行插入断点,并对各变量进行监视。很丢人,我竟然把struts-config.xml文件弄丢了,因此出现了上面的异常,应该是和CVS同步时不小心删除的。

11、“Resources not defined for Validator”
这个是利用Validator插件做验证时可能出现的异常,这时你要检查validation.xml文件,看里面使用的资源是否确实有定义,form的名称是否正确,等等。

上面这些是我在用Struts做项目时遇到过的问题,其中一些曾困绕我不少时间,其实大部分都是自己不细心造成的。希望这篇文章能对你的开发有所帮助,并欢迎继续补充。

Posted by micas on Jun 27th 2007 | Filed in struts | Comments (0)

使用 Spring 更好地处理 Struts 动作

三种整合 Struts 应用程序与 Spring 的方式

George Franciscus (george.franciscus@nexcel.ca), 负责人, Nexcel

 Struts Recipes 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George,他将向您展示如何改变 Struts 动作,使得管理 Struts 动作就像管理 Spring beans 那样。结果是一个增强的 web 框架,这个框架可以方便地利用 Spring AOP 的优势。
您肯定已经听说过控制反转 (IOC) 设计模式,因为很长一段时间以来一直在流传关于它的信息。如果您在任何功能中使用过 Spring 框架,那么您就知道其原理的作用。在本文中,我利用这一原理把一个 Struts 应用程序注入 Spring 框架,您将亲身体会到 IOC 模式的强大。

将一个 Struts 应用程序整合进 Spring 框架具有多方面的优点。首先,Spring 是为解决一些关于 JEE 的真实世界问题而设计的,比如复杂性、低性能和可测试性,等等。第二,Spring 框架包含一个 AOP 实现,允许您将面向方面技术应用于面向对象的代码。第三,一些人可能会说 Spring 框架只有处理 Struts 比 Struts 处理自己好。但是这是观点问题,我演示三种将 Struts 应用程序整合到 Spring 框架的方法后,具体由您自己决定使用哪一种。

我所演示的方法都是执行起来相对简单的,但是它们却具有明显不同的优点。我为每一种方法创建了一个独立而可用的例子,这样您就可以完全理解每种方法。请参阅 下载 部分获得完整例子源代码。请参阅 参考资料,下载 Struts MVC 和 Spring 框架。

为什么 Spring 这么了不起?

Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java™ 企业软件开发,并且提议很多企业难题都能够通过战略地使用 IOC 模式(也称作依赖注入)来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时,结果就产生了 Spring 框架。简言之,Spring 是一个轻型的容器,利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用,留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。

 IOC 和 Spring

IOC 是一种使应用程序逻辑外在化的设计模式,所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合,就像 Spring 框架那样,产生了一种架构,这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 参考资料 了解更多关于 IOC 和 Spring 的信息。
 
 
依赖注入是一个强大的特性,但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器,可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架,并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。

Spring AOP 允许您使用拦截器 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础,所以拦截器广泛用于日志记录。您很快就会看到,为了处理横切关注点,Spring AOP 发布了它自己的拦截器,您也可以编写您自己的拦截器。

整合 Struts 和 Spring

与 Struts 相似,Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点,尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力,以至于开发团队宁愿整合 Spring 框架的特性,而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。

在接下来的小窍门中,您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用,我将会向您展示一个令人兴奋的应用程序,这个程序使用的是这三种方法中我最喜欢的一种。

三个小窍门

接下来的每种整合技术(或者窍门)都有自己的优点和特点。我偏爱其中的一种,但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候,这将给您提供一个广阔的选择范围。方法如下:

使用 Spring 的 ActionSupport 类整合 Structs
使用 Spring 的 DelegatingRequestProcessor 覆盖 Struts 的 RequestProcessor
将 Struts Action 管理委托给 Spring 框架
装载应用程序环境

无论您使用哪种技术,都需要使用 Spring 的 ContextLoaderPlugin 为 Struts 的 ActionServlet 装载 Spring 应用程序环境。就像添加任何其他插件一样,简单地向您的 struts-config.xml 文件添加该插件,如下所示:

<plug-in className=
  “org.springframework.web.struts.ContextLoaderPlugIn”>
    <set-property property=
      “contextConfigLocation” value=”/WEB-INF/beans.xml”/>
 </plug-in>
 
前面已经提到过,在 下载 部分,您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点,但是您也可以下载应用程序以查看所有的细节。
 回页首
 

窍门 1. 使用 Spring 的 ActionSupport

手动创建一个 Spring 环境是一种整合 Struts 和 Spring 的最直观的方式。为了使它变得更简单,Spring 提供了一些帮助。为了方便地获得 Spring 环境,org.springframework.web.struts.ActionSupport 类提供了一个 getWebApplicationContext() 方法。您所做的只是从 Spring 的 ActionSupport 而不是 Struts Action 类扩展您的动作,如清单 1 所示:

清单 1. 使用 ActionSupport 整合 Struts

package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import org.springframework.context.ApplicationContext;
import org.springframework.web.struts.ActionSupport;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends ActionSupport {   |(1)
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get(”isbn”);
  
    //the old fashion way
    //BookService bookService = new BookServiceImpl();
  
    ApplicationContext ctx =
      getWebApplicationContext();    |(2)
    BookService bookService =
      (BookService) ctx.getBean(”bookService”);   |(3)
       
  Book book = bookService.read(isbn.trim());
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
        (”message.notfound”));
      saveErrors(request, errors);
      return mapping.findForward(”failure”) ;
  }
    request.setAttribute(”book”, book);
    return mapping.findForward(”success”);
  }
}
 
让我们快速思考一下这里到底发生了什么。在 (1) 处,我通过从 Spring 的 ActionSupport 类而不是 Struts 的 Action 类进行扩展,创建了一个新的 Action。在 (2) 处,我使用 getWebApplicationContext() 方法获得一个 ApplicationContext。为了获得业务服务,我使用在 (2) 处获得的环境在 (3) 处查找一个 Spring bean。

这种技术很简单并且易于理解。不幸的是,它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring,那么您必须重写代码。并且,由于 Struts 动作不在 Spring 的控制之下,所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时,这种技术可能有用,但是在大多数情况下,这种方法不如另外两种方法合适。
 回页首
 

窍门 2. 覆盖 RequestProcessor

将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 org.springframework.web.struts.DelegatingRequestProcessor 类来覆盖 Struts 的 RequestProcessor 处理程序,如清单 2 所示:

清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合

<?xml version=”1.0″ encoding=”ISO-8859-1″ ?>
<!DOCTYPE struts-config PUBLIC
          “-//Apache Software Foundation//DTD Struts Configuration 1.1//EN”
          “http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd“>
<struts-config>
 <form-beans>
    <form-bean name=”searchForm”
      type=”org.apache.struts.validator.DynaValidatorForm”>
               <form-property name=”isbn”    type=”java.lang.String”/>
    </form-bean>
 
  </form-beans>
 <global-forwards type=”org.apache.struts.action.ActionForward”>
     <forward   name=”welcome”                path=”/welcome.do”/>
     <forward   name=”searchEntry”            path=”/searchEntry.do”/>
     <forward   name=”searchSubmit”           path=”/searchSubmit.do”/>
 </global-forwards>
 <action-mappings>
    <action    path=”/welcome” forward=”/WEB-INF/pages/welcome.htm”/>
    <action    path=”/searchEntry” forward=”/WEB-INF/pages/search.jsp”/>
    <action    path=”/searchSubmit”
               type=”ca.nexcel.books.actions.SearchSubmit”
               input=”/searchEntry.do”
               validate=”true”
               name=”searchForm”>
              <forward name=”success” path=”/WEB-INF/pages/detail.jsp”/>
              <forward name=”failure” path=”/WEB-INF/pages/search.jsp”/>
    </action> 
 </action-mappings>
 <message-resources parameter=”ApplicationResources”/>
 <controller processorClass=”org.springframework.web.struts.
   DelegatingRequestProcessor”/> |(1)
 <plug-in className=”org.apache.struts.validator.ValidatorPlugIn”>
    <set-property property=”pathnames”
      value=”/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml”/>
 </plug-in>
 <plug-in className=”org.springframework.web.struts.ContextLoaderPlugIn”>
    <set-property property=”csntextConfigLocation” value=”/WEB-INF/beans.xml”/>
 </plug-in>
 
</struts-config>
 
我利用了 <controller> 标记来用 DelegatingRequestProcessor 覆盖默认的 Struts RequestProcessor。下一步是在我的 Spring 配置文件中注册该动作,如清单 3 所示:

清单 3. 在 Spring 配置文件中注册一个动作

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN”
  “http://www.springframework.org/dtd/spring-beans.dtd“>
<beans>
  <bean id=”bookService” class=”ca.nexcel.books.business.BookServiceImpl”/>
  <bean name=”/searchSubmit”
    class=”ca.nexcel.books.actions.SearchSubmit”> |(1)
     <property name=”bookService”>
        <ref bean=”bookService”/>
     </property>
  </bean>
</beans>
 
注意:在 (1) 处,我使用名称属性注册了一个 bean,以匹配 struts-config 动作映射名称。SearchSubmit 动作揭示了一个 JavaBean 属性,允许 Spring 在运行时填充属性,如清单 4 所示:

清单 4. 具有 JavaBean 属性的 Struts 动作

package ca.nexcel.books.actions;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import ca.nexcel.books.beans.Book;
import ca.nexcel.books.business.BookService;
public class SearchSubmit extends Action {
 
  private BookService bookService;
  public BookService getBookService() {
    return bookService;
  }
  public void setBookService(BookService bookService) { | (1)
    this.bookService = bookService;
  }
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException {
    DynaActionForm searchForm = (DynaActionForm) form;
    String isbn = (String) searchForm.get(”isbn”);
  
  Book book = getBookService().read(isbn.trim());  |(2)
    if (null == book) {
      ActionErrors errors = new ActionErrors();
      errors.add(ActionErrors.GLOBAL_ERROR,new ActionError(”message.notfound”));
      saveErrors(request, errors);
      return mapping.findForward(”failure”) ;
  }
      request.setAttribute(”book”, book);
      return mapping.findForward(”success”);
  }
}
 
在清单 4 中,您可以了解到如何创建 Struts 动作。在 (1) 处,我创建了一个 JavaBean 属性。DelegatingRequestProcessor自动地配置这种属性。这种设计使 Struts 动作并不知道它正被 Spring 管理,并且使您能够利用 Sping 的动作管理框架的所有优点。由于您的 Struts 动作注意不到 Spring 的存在,所以您不需要重写您的 Struts 代码就可以使用其他控制反转容器来替换掉 Spring。

DelegatingRequestProcessor 方法的确比第一种方法好,但是仍然存在一些问题。如果您使用一个不同的 RequestProcessor,则需要手动整合 Spring 的 DelegatingRequestProcessor。添加的代码会造成维护的麻烦并且将来会降低您的应用程序的灵活性。此外,还有过一些使用一系列命令来代替 Struts RequestProcessor 的传闻。 这种改变将会对这种解决方法的使用寿命造成负面的影响。
 回页首
 

窍门 3. 将动作管理委托给 Spring

一个更好的解决方法是将 Strut 动作管理委托给 Spring。您可以通过在 struts-config 动作映射中注册一个代理来实现。代理负责在 Spring 环境中查找 Struts 动作。由于动作在 Spring 的控制之下,所以它可以填充动作的 JavaBean 属性,并为应用诸如 Spring 的 AOP 拦截器之类的特性带来了可能。

清单 5 中的 Action 类与清单 4 中的相同。但是 struts-config 有一些不同:

清单 5. Spring 整合的委托方法

<?xml version=”1.0″ encoding=”ISO-8859-1″ ?>
<!DOCTYPE struts-config PUBLIC
          “-//Apache Software Foundation//DTD Struts Configuration 1.1//EN”
          “http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd“>
<struts-config>
 <form-beans>
    <form-bean name=”searchForm”
      type=”org.apache.struts.validator.DynaValidatorForm”>
               <form-property name=”isbn”    type=”java.lang.String”/>
    </form-bean>
 
  </form-beans>
 <global-forwards type=”org.apache.struts.action.ActionForward”>
     <forward   name=”welcome”                path=”/welcome.do”/>
     <forward   name=”searchEntry”            path=”/searchEntry.do”/>
     <forward   name=”searchSubmit”           path=”/searchSubmit.do”/>
 </global-forwards>
 <action-mappings>
    <action    path=”/welcome” forward=”/WEB-INF/pages/welcome.htm”/>
    <action    path=”/searchEntry” forward=”/WEB-INF/pages/search.jsp”/>
    <action    path=”/searchSubmit”
             type=”org.springframework.web.struts.DelegatingActionProxy” |(1)
             input=”/searchEntry.do”
             validate=”true”
             name=”searchForm”>
             <forward name=”success” path=”/WEB-INF/pages/detail.jsp”/>
             <forward name=”failure” path=”/WEB-INF/pages/search.jsp”/>
    </action> 
 </action-mappings>
 <message-resources parameter=”ApplicationResources”/>
 <plug-in className=”org.apache.struts.validator.ValidatorPlugIn”>
    <set-property
    property=”pathnames”
    value=”/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml”/>
 </plug-in>
 <plug-in
    className=”org.springframework.web.struts.ContextLoaderPlugIn”>
    <set-property property=”contextConfigLocation” value=”/WEB-INF/beans.xml”/>
 </plug-in>
 
</struts-config>
 
清单 5 是一个典型的 struts-config.xml 文件,只有一个小小的差别。它注册 Spring 代理类的名称,而不是声明动作的类名,如(1)处所示。DelegatingActionProxy 类使用动作映射名称查找 Spring 环境中的动作。这就是我们使用 ContextLoaderPlugIn 声明的环境。

将一个 Struts 动作注册为一个 Spring bean 是非常直观的,如清单 6 所示。我利用动作映射使用 <bean> 标记的名称属性(在这个例子中是 “/searchSubmit”)简单地创建了一个 bean。这个动作的 JavaBean 属性像任何 Spring bean 一样被填充:

清单 6. 在 Spring 环境中注册一个 Struts 动作

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN”
 ”http://www.springframework.org/dtd/spring-beans.dtd“>
<beans>
  <bean id=”bookService” class=”ca.nexcel.books.business.BookServiceImpl”/>
  <bean name=”/searchSubmit”  
        class=”ca.nexcel.books.actions.SearchSubmit”>
     <property name=”bookService”>
        <ref bean=”bookService”/>
     </property>
  </bean>
</beans>
 

动作委托的优点

动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring,不对代码作任何改变就可用于非 Spring 应用程序中。RequestProcessor 的改变不会影响它,并且它可以利用 Spring AOP 特性的优点。

动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作,您就可以使用 Spring 给动作补充更强的活力。例如,没有 Spring 的话,所有的 Struts 动作都必须是线程安全的。如果您设置 <bean> 标记的 singleton 属性为“false”,那么不管用何种方法,您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性,但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如,当实例化 Struts 动作时,<bean> 标记的 init-method 属性被用于运行一个方法。类似地,在从容器中删除 bean 之前,destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法,它们以一种与 Servlet 生命周期相同的方式进行管理。

 拦截 Struts

前面提到过,通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是:您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作,您可以用最小的代价处理横切关注点。

虽然 Spring 提供很多内置拦截器,但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器,您需要做三件事:

创建拦截器。
注册拦截器。
声明在何处拦截代码。
这看起来非常简单的几句话却非常强大。例如,在清单 7 中,我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话:

清单 7. 一个简单的日志记录拦截器

package ca.nexcel.books.interceptors;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LoggingInterceptor implements MethodBeforeAdvice {
   public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(”logging before!”);
    }
}
 
这个拦截器非常简单。before() 方法在拦截点中每个方法之前运行。在本例中,它打印出一句话,其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器,如清单 8 所示:

清单 8. 在 Spring 配置文件中注册拦截器

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN”
  “http://www.springframework.org/dtd/spring-beans.dtd“>
<beans>
  <bean id=”bookService” class=”ca.nexcel.books.business.BookServiceImpl”/>
  <bean name=”/searchSubmit”
        class=”ca.nexcel.books.actions.SearchSubmit”>
     <property name=”bookService”>
        <ref bean=”bookService”/>
     </property>
  </bean>
  <!–  Interceptors –>
  <bean name=”logger”   
    class=”ca.nexcel.books.interceptors.LoggingInterceptor”/> |(1)
  <!– AutoProxies –>
  <bean name=”loggingAutoProxy”
        class=”org.springframework.aop.framework.autoproxy.
          BeanNameAutoProxyCreator”> |(2)
    <property name=”beanNames”>
          <value>/searchSubmit</valuesgt; |(3)
    </property>
    <property name=”interceptorNames”>
        <list>
          <value>logger</value> |(4)
        </list>
    </property>
   </bean>
</beans>
 
您可能已经注意到了,清单 8 扩展了 清单 6 中所示的应用程序以包含一个拦截器。具体细节如下:

在 (1) 处,我注册了这个拦截器。
在 (2) 处,我创建了一个 bean 名称自动代理,它描述如何应用拦截器。还有其他的方法定义拦截点,但是这种方法常见而简便。
在 (3) 处,我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作,则只需要在 “beanNames” 下面创建附加的 <value> 标记。
在 (4) 处,当拦截发生时,我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于“beanNames”。
就是这样。就像这个例子所展示的,将您的 Struts 动作置于 Spring 框架的控制之下,为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中,使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。

结束语

在本文中,您已经学习了将 Struts 动作整合到 Spring 框架中的三种窍门。使用 Spring 的 ActionSupport 来整合 Struts(第一种窍门中就是这样做的)简单而快捷,但是会将 Struts 动作与 Spring 框架耦合在一起。如果您需要将应用程序移植到一个不同的框架,则需要重写代码。第二种解决方法通过委托 RequestProcessor 巧妙地解开代码的耦合,但是它的可扩展性不强,并且当 Struts 的 RequestProcessor 变成一系列命令时,这种方法就持续不了很长时间。第三种方法是这三种方法中最好的:将 Struts 动作委托给 Spring 框架可以使代码解耦,从而使您可以在您的 Struts 应用程序中利用 Spring 的特性(比如日志记录拦截器)。

三种 Struts-Spring 整合窍门中的每一种都被实现成一个完整可用的应用程序。请参阅 下载 部分仔细研究它们。

 http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main

Posted by micas on Jun 27th 2007 | Filed in