|
![]() |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
摘要: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创建留言本(一) |
![]() ![]() |
快精灵印艺坊 版权所有 |
首页![]() ![]() ![]() ![]() ![]() ![]() ![]() |