快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

使用JAVABEAN高效处理JSP

摘要:JavaServer Pages Model II的一个基本思想就是将表现(HTML)和处理逻辑分开。这篇文章介绍的是一个高效、可重用的设计方式,将动态的内容、处理和验证由JavaServer Page中分离出来,放到一个相应的JavaBean中。它使用Template Method的设计方式,可提高代码在整个Web应用中的重用性。此外,它还介绍了在一个HTTP Session中bean间如何通信的简朴技巧。

  JavaServer Pages(JSP)技术提供了许多的特性,可让你简易和迅速地开发Web应用。不过,假如你无计划和结构地使用这些技术,你的JSP代码将会是各种HTML标记、JSP标记和Java代码的混合,很难于看懂、调试和维护。

  这里的目标是将全部的JSP处理代码封装到JavaBean中。这种方式的好处是HTML编程者和美工可以做表现的开发(使用HTML编辑器),而Java编程者可以集中开发编程逻辑。此外,这种方式可便于你为同一个Web应用提供不同的外观。

  我将介绍的架构使用Template Method方式,可集中实现整个应用的共有设计部分和实现每个JSP的共有处理。就这个例子,共有的处理包括有页面状态治理、共有的页面处理、共有的错误处理,以及在页面间共享信息的技术。所有这些仅是定义一次,你可在页面设计时再处理特定的细节。

  我将以一个简朴的"投票"应用为例,介绍如何使用这个架构。你应有基本的JSP和Java知识,并且最好懂得一些UML知识。

  总的静态结构

  这个部分将介绍该架构核心部分的概观,以及投票应用的例子。图一显示了该架构的一个UML框图:



**************图一*******************
(UML类框图)

  该架构的中央部分由两个共有的JSP包含文件和两个类组成,下面将会谈到。它们的作用是完成共有的处理。

   includeheader.jsp:这个JSP文件必须被静态包含在每个JSP文件的开头。

   includefooter.jsp:这个JSP文件必须被静态包含在每个JSP文件的末尾。

  AbstractJSPBean:这是一个抽象类,你应该将它作为所有JSP JavaBean类的一个超类使用。它是该架构的核心类。

   SharedSessionBean:在一个HTTP session中,为所有的JSP JavaBean对象提供关联。

  JSP网页只是用作表现的。每个JSP页面都应该使用一个相应的JavaBean来处理特定页面的逻辑。每个JSP页面都应该静态包含includeheader.jsp和includefooter.jsp。每个JavaBean都必须扩展AbstractJSPBean,该抽象类包含有模板方式,可完成共有的处理。

  这个投票的应用包含有以下的JSP和相应的JavaBean:

   login.jsp, LoginJSPBean:投票者认证和登录

   vote.jsp, VoteJSPBean: 执行投票

   confirmation.jsp, ConfirmationJSPBean: 显示确认和投票的结果

  我将不会具体讨论数据库和商业逻辑的部分((Voter, Candidate和VoteDB),不过它们对于这个例子是必需的。

  以上我们已经对整体有了一个概观,接着我们将讨论例子中的每个JSP页面。

  JSP例子

  每个页面都必须使用指定的结构,以符合整个架构。

  列表1。login.jsp

<%@ page import = "lbm.jsputil.*" %>
<jsp:useBean id="_loginJSPBean" class="lbm.examples.LoginJSPBean"
scope="session"/>
<jsp:setProperty name="_loginJSPBean" property="*"/>
<% AbstractJSPBean _abstractJSPBean = _loginJSPBean; %>
<%@ include file="includeheader.jsp" %>

<html>
<head><title>Vote Login</title></head>
<body bgcolor="white">

<font size=4>
Please enter your Voter ID and Password

</font>

<font size="3" color="Red">
<jsp:getProperty name="_loginJSPBean" property="errorMsg"/>

</font>

<font size=3>
<form method=post>
Voter ID <input type=text name=voterId value=<jsp:getProperty
name="_loginJSPBean" property="voterId"/>>

Password <input type=password name=password value=<jsp:getProperty
name="_loginJSPBean" property="password"/>>


<input type=submit value="Login">
</form>
</font>
</body>
</html>

<%@ include file="includefooter.jsp" %>


  该JSP页面的架构如下:由几个JSP语句开始。接着的HTML代码将没有多少JSP指令、语句和脚本等。除了几个必要的指令,它们负责由bean中得到动态的内容。最后,页面使用了一个JSP include指令。

  我们讨论其中一些重要的JSP语句:

<jsp:useBean id="_loginJSPBean" class="lbm.examples.LoginJSPBean" scope="session"/>
<jsp:setProperty name="_loginJSPBean" property="*"/>


以上的代码在JSP和相应的bean间建立了一个连接。第二个语句显式传送全部的form字段(存储为HTTP request参数)到bean中匹配的属性中。代码中使用了bean的setter方式。

<% AbstractJSPBean _abstractJSPBean = _loginJSPBean; %>
<%@ include file="includeheader.jsp" %>


  第一个语句让includeheader.jsp可执行共有的处理。第二个语句将includeheader.jsp静态包含进来。要注重到loginJSPBean和_abstractJSPBean现在指向同样的对象,只是带有不同的接口。

  列表2:includeheader.jsp

<%-- Set the SharedSessionBean --%>
<jsp:useBean id="_sharedSessionBean" class="lbm.jsputil.SharedSessionBean" scope="session"/>
<% _abstractJSPBean.setSharedSessionBean(_sharedSessionBean); %>

<%-- Set implicit Servlet objects --%>
<% _abstractJSPBean.setRequest(request); %>
<% _abstractJSPBean.setResponse(response); %>
<% _abstractJSPBean.setServlet(this); %>

<%-- Perform the processing associated with the JSP --%>
<% _abstractJSPBean.process(); %>

<%-- If getSkipPageOutput equals false, do not output the JSP page --%>
<% if (! _abstractJSPBean.getSkipPageOutput()) { %>



  includeheader.jsp是模板的核心元素之一。所有的JSP页面都使用这个共有的元素。

  列表2的前两个语句令不同页面但在同一HTTP session中的JSP bean之间互相进行通信。基本上,每个JSP将有两个与它关联的JavaBean:一个指定的JSP JavaBean(例如,LoginJSPBean)和共有的SharedSessionBean。SharedSessionBean作为一个共有的元素使用,用来连接所有的页面;我将在后面继承讨论它。

  includeheader.jsp中接着的三个语句与固有的Servlet对象有关。

<% _abstractJSPBean.setRequest(request); %>
<% _abstractJSPBean.setResponse(response); %>
<% _abstractJSPBean.setServlet(this); %>


  JSP规范提供访问Java Servlet规范中的固有对象,例如页面处理中常用到的request、response和servlet对象。因此它们被传送到JSP bean。

  <% _abstractJSPBean.process(); %>

  最后,通过上面的语句来触发相关JSP页面的处理。你看到我们调用的是抽象JSP bean上的方式,而不是实类LoginJSPBean上的。为什么?我将在以下的部分解释。

  运用Template Method设计方式

  AbstractJSPBean是Template Method设计的主体。每个实际的JSP JavaBean都必须继续这个类。

  列表 3. AbstractJSPBean.java

package lbm.jsputil;

import java.util.*;
import javax.servlet.http.*;
import javax.servlet.*;

public abstract class AbstractJSPBean {

/* constants used for _state */
public static final int NEW = 0;
public static final int FIRSTPASS = 1;
public static final int PROC = 2;
public static final int ERR = -1;

private int _state; // current state
private String _errorMsg; // current message that is being appended during validation
private boolean _skipPageOutput; // should the page output be skipped

private SharedSessionBean _sharedSessionBean; // used for associating the JSP Bean with the HTTP Session

/* standard Servlet objects that need to be setup for each JSP Bean */
protected HttpServletRequest _request;
protected HttpServletResponse _response;
protected Servlet _servlet;

public AbstractJSPBean () {
setState(NEW);
}

protected abstract void beanProcess() throws java.io.IOException;

protected abstract void beanFirstPassProcess() throws java.io.IOException;

protected abstract void beanFooterProcess() throws java.io.IOException;

protected abstract String getJSPCode();

public void process() throws java.io.IOException {
setSkipPageOutput(false); // by default do not skip page output. Specific bean process
// methods can override it.
if (getState() == NEW) {
setState(FIRSTPASS);
beanFirstPassProcess();
} else {
resetErrorMsg();
setState(PROC);
beanProcess();
}

// validation that all common fields have been properly set by the application
// this is actually checking that the code has been written properly
String l_err = "";
if (_sharedSessionBean == null) l_err = l_err + "; SharedSessionBean must be set";
if (_request == null) l_err = l_err + "; Request must be set";
if (_response == null) l_err = l_err + "; Response must be set";
if (_servlet == null) l_err = l_err + "; Servlet must be set";
if ( ! l_err.equals("")) throw new IllegalStateException(l_err);
}

public void footerProcess() throws java.io.IOException {
beanFooterProcess();
}

protected void addErrorMsg (String addErrorMsg) {
if (_errorMsg == null) _errorMsg = addErrorMsg;
else _errorMsg = _errorMsg + " <br> " + addErrorMsg;

setState(ERR);
}
protected void resetErrorMsg () {
_errorMsg = null;
}
public String getErrorMsg () {
if (_errorMsg == null) return "";
else return _errorMsg;
}

protected void setState (int newState) {
_state = newState;
}
public int getState () {
return _state;
}

public void setSharedSessionBean (SharedSessionBean newSharedSessionBean) {
if (_sharedSessionBean == null) {
_sharedSessionBean = newSharedSessionBean;
_sharedSessionBean.putJSPBean(getJSPCode(), this);
} else {
if (_sharedSessionBean != newSharedSessionBean) {
throw new IllegalStateException("SharedSessionBean is not set properly. SharedSessionBean must be the same for all PageBeans within the session");
}
}
}
public SharedSessionBean getSharedSessionBean () {
return _sharedSessionBean;
}

public void setSkipPageOutput (boolean newSipPageOutput) {
_skipPageOutput = newSipPageOutput;
}
public boolean getSkipPageOutput () {
return _skipPageOutput;
}

protected void redirect (String redirectURL) throws java.io.IOException {
// skip the page output since we are redirecting
setSkipPageOutput(true);
_response.sendRedirect(redirectURL);
}

public void setRequest (HttpServletRequest newRequest) {
_request = newRequest;
}
public void setResponse (HttpServletResponse newResponse) {
_response = newResponse;
}
public void setServlet (Servlet newServlet) {
_servlet = newServlet;
}
}


AbstractJSPBean包含有以下的抽象方式:beanFirstPassProcess(), beanProcess(), and beanFooterProcess()。这些方式被称为primitive方式。你必须在实际的JSP JavaBean子类中实现它们。每个都在JSP处理的一个特定阶段中执行。

  beanFirstPassProcess()--在页面被首次调用时进行的处理,它发生在页面开始输出之前。它适合用来初始化动态的内容和验证对页面的访问。可参见VoteJSPBean中该方式的实现,该Bean中用它来验证页面的访问,并且进行应用的流程控制。

  beanProcess()--发生在第二和后来的页面调用期间的处理,在页面输出开始之前。你可以用它来作HTML form验证和数据库更新。在LoginJSPBean类中,该方式被用作HTML form处理,在VoteJSPBean类中,用来保存信息到数据库中。

  beanFooterProcess()--在页面输出完成后进行的处理。你可以使用它来令session无效。在ConfirmationJSPBean类中,当投票完成后,通过实现该方式令session无效,并且显示确认的页面。

  接着我们将看一下process()方式:

public void process() throws java.io.IOException {
setSkipPageOutput(false); // by default do not skip page output. Specific bean process
// methods can override it.
if (getState() == NEW) {
setState(FIRSTPASS);
beanFirstPassProcess();
} else {
resetErrorMsg();
setState(PROC);
beanProcess();
}
....



  process()首先检查JSP的状态;然后,根据状态,它调用相应的primitive方式。它还设置了JSP相应的状态。

  process()和footerProcess()方式被称为template方式。它们由JSP中真正调用(在includeheader.jsp和includefooter.jsp中)。实体的bean不应该覆盖它们。template方式包含有共有的框架算法。一个典型模板方式的框架算法执行一个共有的处理,并且调用primitive(抽象)方式(beanFirstPassProcess()、beanProcess()和beanFooterProcess()),这些方式的实现在每个实际的JSP JavaBean中都是不同的。框架算法也可以称为AbstractJSPBean中实现的实体方式。以上的规则是Template Method设计方式的基本点。

  这种方式的好处是:

  1、通过在模板方式中分解出共有的处理,你可以做到代码重用

  2、你可以对整个应用进行共有的设计和处理

除了处理逻辑外,AbstractJSPBean还包含有以下详细的方式来帮助子类(实际的JSP JavaBean)来实现它们的处理任务,你不应该覆盖这些实际的方式。

  1、与用户错误治理相关的方式(addErrorMsg(), resetErrorMsg(), and getErrorMsg())

  2、与页面状态治理相关的方式(setState(), getState())

  3、治理与SharedSessionBean关联的方式


  4、控制JSP页面的HTML部分是否输出的方式(setSkipPageOutput(), getSkipPageOutput())

  5、重定向的方式

  6、访问Servlet对象的方式: request, response和servlet

form处理、动态内容和bean通信

  列表4展示了一个详细的JSP JavaBean--LoginJSPBean,用来实现特定的页面处理

  列表4。LoginJSPBean


package lbm.examples;

import lbm.jsputil.*;
import java.util.*;

public class LoginJSPBean extends AbstractJSPBean {

public static final String PAGE_CODE = "login";

private String _voterId;
private String _password;
private Voter _voter = null;

public LoginJSPBean() {
}

public void setVoterId (String newVoterId) {
_voterId = newVoterId;
}

public String getVoterId() {
if (_voterId == null) return "";
else return _voterId;
}

public void setPassword (String newPassword) {
_password = newPassword;
}

public String getPassword() {
if (_password == null) return "";
else return _password;
}

public Voter getVoter () {
return _voter;
}

protected void beanProcess () throws java.io.IOException {
if (_voterId == null || _voterId.equals("")) {
addErrorMsg("Voter must be entered");
}

if (_password == null || _password.equals("")) {
addErrorMsg("Password must be entered");
}

if (getState() != ERR) {
file://If all the fields are entered, try to login the voter
Voter voter = VoteDB.login(_voterId, _password);
if (voter == null) {
addErrorMsg("Unable to authenticate the Voter. Please try again.");
}
else {
_voter = voter;

if (_voter.getVotedForCandidate() != null) {
// if the voter has already voted, send the voter to the last page
redirect("confirmation.jsp");
}
else {
// go to the Vote page
redirect("vote.jsp");
}
}
}
}

protected void beanFirstPassProcess() throws java.io.IOException {
}

protected void beanFooterProcess() throws java.io.IOException {
}

protected String getJSPCode() {
return PAGE_CODE;
}
}



  观察LoginJSPBean类中的set和get方式。就象上面提及的,它们用作动态的匹配,并且用来在form字段(request参数)和bean属性间传送值。

  列表4中的beanProcess()方式,展示了form处理的一些基本点。这个方式发生在页面输出前,在第二和全部后来的页面调用期间执行。这意味着它将仅在用户按下登录按钮并且提交form后执行。

  你首先要验证voteId和password的输入,产生的错误将通过addErrorMsg方式记录下来。这个方式设置AbstractJSPBean类的errorMsg属性。该属性可被JSP用来显示用户的错误:

   <jsp:getProperty name="_loginJSPBean" property="errorMsg"/>

  假如数据的输入成功通过,beanProcess()方式将会调用数据库来验证用户。最后,它通过调用AbstractJSPBean类中实现的redirect()方式,将哀求重定向到相应的页面。

  以下我们将讨论VoteJSPBean类中的一些方式。它们将可以解释该架构的一些其它方面,例如JSP JavaBean之间的通信和应用的流程控制。

  列表5。VoteJSPBean类中的beanFirstPassProcess()


protected void beanFirstPassProcess() throws java.io.IOException {
// get the Voter from Login page
_voter = null;

LoginJSPBean loginJSPBean =
(LoginJSPBean) getSharedSessionBean().getJSPBean(LoginJSPBean.PAGE_CODE);

if (loginJSPBean != null) {
_voter = loginJSPBean.getVoter();
}

if (_voter == null) {
// voter is not logged in yet. Send it to Login page
setState(NEW);
redirect("login.jsp");
}
}



以上的方式使用了AbstractJSPBean类中_sharedSessionBean对象。SharedSessionBean类通过使用一个简朴的方式,让所有的JSP JavaBean对象在一个HTTP session中进行通信。它保存有一个session内的全部JSP JavaBean中的一个Map。Map是Java Collections框架的一个接口,它是Java 1.2推出的。对认识Java 1.1的人来说,它与Hashtable异常类似。一个JSP JavaBean的主键是它的PAGE_CODE,它作为一个常数存储在每个JSP JavaBean类中。

  在这个例子中,beanFirstPassProcess()方式首先定位到LoginJSPBean对象。接着,它由LoginJSPBean对象中得到Voter对象,并且存储一个到它的引用,以便以后使用。假如Voter为null,这意味着用户没有首先登录就进入Voter页面,因此它重定向到登录页面。这是一个应用流程控制的简朴例子。你可以设计更复杂的方式,例如使用一个智能的调度程序,不过这些讨论已经超出了本文的范围。

  列表6。VoteJSPBean类的getCandidateList()方式


public String getCandidateList () {
StringBuffer candidateList = new StringBuffer();
Candidate candidate;

Iterator candidates = VoteDB.getCandidates();
while (candidates.hasNext()) {
candidate = (Candidate) candidates.next();

candidateList.append("<input type=radio name="candidateName" value="");
candidateList.append(candidate.getName());
candidateList.append(""> ");
candidateList.append(candidate.getName());
candidateList.append("<br> ");
}

return candidateList.toString();
}



  以上的getCandidateList()方式被vote.jsp调用,通过以下的方式:

   <jsp:getProperty name="_voteJSPBean" property="candidateList"/>

  根据由数据库得到的内容不同,该方式提供不同的动态HTML内容输出。它需要开发JavaBean的Java编程者懂得一些HTML知识。

  你也可以使用一个利用HTML的独立库来格式化HTML,它可以接受一个预定义的输入。例如Iterator,然后以预定义的格式产生HTML输出。另一个方式是使用标签库。

  最后的要点:框架

  通过将表现和逻辑分离开来,该架构可让你独立地修改表现(JSP)和逻辑(bean)。这意味着你可以修改bean中的逻辑而无需改动JSP,只要你保持bean的属性不变就行了。相反也是成立的,你可以将JSP代码交给HTML开发者和美工来改变站点的外观,而不会影响其中的Java代码。

  你可以轻微修改该框架的核心元素来满意你应用的特殊需要。你可以加入新的或者修改现有的方式,或者修改模板的方式。重要的一点是,在你的整个应用中,全部的JSP和JSP JavaBean都应该使用这个架构。

  在刚开始时,这个架构看来比较复杂,特殊是对于一个只有三页的例子应用来说。不过,假如你开始写你的应用,你将会发现当应用变复杂时,代码量的增长却没有预期的大。

  这个架构并没有谈到Web应用中通常用到的多层设计。它主要集中在JSP的表现层。要建立真正的三层或者多层的系统,JSP JavaBeamn将需要调用Enterprise JavaBean或者一些其它的商业逻辑实现。

  例子还介绍了让应用跟踪HTTP session的架构。不过,假如你不想依靠session的话,该架构仍旧可以工作得很好。应用的页面和流程应该是分别设计的。你可能将不需要SharedSessionBean。你的页面只是用作表现,而独立的页面将仅用作处理和验证,而不会有任何的HTML输出。为此,你将主要使用beanFirstPassProcess() 方式。

  最后的要点:例子

  我使用Tomcat3.1来测试这个例子,Tomcat3.1符合JSP1.1和Servlet2.2规范。这篇文章将不会讨论如何在Tomcat中配置JSP应用的具体情况。

  在测试该例子时,你可以在VoteDB类的源代码中得到Voter ID,以便可以进行登录测试(密码和ID是相同的)。

  你必须在浏览器中答应cookies,否则不能成功运行例子。假如你需要在禁止cookies时仍旧可以运行这个应用,你必须重新写URL(使用the javax.servlet.http.HttpServletResponse类的encodeURL()方式)。你需要重写全部应用中的URL,包括你的JSP中的链接,form标记中的action,以及在JSP JavaBean中用来重定向URL的HTTP哀求。

  结论

  这篇文章介绍的架构为JSP的应用设计提供了一个全面的解决办法。它改进了代码重用,确定了应用的体系,并且便于扩展。一个最大的好处是将表现和逻辑分开,你可以独立改变它们,而不会影响另一方。 
返回类别: 教程
上一教程: 解决JSP中文显示问题的几个方式
下一教程: 当TOMCAT遭遇JBDC DRIVER,出现 \'CANNOT CREATE RESOURCE INSTANCE\'!

您可以阅读与"使用JAVABEAN高效处理JSP"相关的教程:
· 使用JSP + JAVABEAN + XML 开发的一个例子
· 学习在JSP中使用JAVABEANS
· 可以使用多个jsp定制标签在JSP中达到接近servelt的处理效果
· 在Struts中使用JavaBean和List(多行数据)类型属性
· 如何使用JSP+MYSQL创建留言本(一)
    微笑服务 优质保证 索取样品