JSP最佳实践告诉我们,JSP应当仅仅作为一个表现层的表示工具,不应当使JSP牵扯到业务层的东西。由于在JSP1.0标准中,脚本元素的使用不可避免,这个最佳实践也就成为空话。然而,JSP2.0已经可以让我们这么做了。下面将给出一个不含任何脚本元素的JSP页面。在这个页面中,JSP仅仅用来显示和传递数据,所有的业务处理,数据库的访问,都是通过JavaBean实现的。

这个试验是从一个页面获得name和age两个值,然后通过bean录入数据库。试验包含3个文件,并不是每个文件都有显示功能。下面将进行一一介绍。

1、Controller.jsp
这是一个前端控制器,用于控制转向的页面。使用JSTL技术进行控制,避免了脚本元素。
********************
<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<c:choose>
    <c:when test="${empty param.action}">
        <jsp:forward page="RegisterMember.jsp"/>
    </c:when>
</c:choose>
********************
由上面的代码可以清楚地看出,当param.action为空时(这是由一个EL的empty函数计算的),页面将转向RegisterMember.jsp。事实上,由于刚刚载入页面的时候,param.action当然是空的,因此,第一个页面一定会正确地转向RegisterMember.jsp。

2、RegisterMember.jsp
这是一个具有显示的页面,用于数据的输入。这个JSP实际上是一个很简单的HTML文档。
********************
<html>
    <head>
        <title>Register Member</title>
    </head>

<body>
    <h1>
        Register member
    </h1>
    

    <form action="reg.jsp" method="post">
        name:
        <input type="text" maxlength="20" name="name">
        

        &nbsp;age:
        <input type="text" maxlength="5" name="age">
        

        

        <input type="submit" value="Submit" name="submit">
        

    </form>
</body>
</html>
********************
注意,这里的form的action指向的即下一个JSP页面。

3、reg.jsp
这个JSP进行数据的处理,因而可以算作是核心的JSP文档。
********************
<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>
<%@ taglib prefix="eShop" uri="http://www.galaxy_OPEN.org/eShop/eShop-taglib" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <base href="<%=basePath%>">

        <title>Register Member</title>

        <meta http-equiv="pragma" content="no-cache">
        <meta http-equiv="cache-control" content="no-cache">
        <meta http-equiv="expires" content="0">
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="This is my page">
        <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

    </head>

    <body>
        <!--下面绿色部分仅仅是参数的再显示,不进行业务处理-->
        name: ${param.name }
        

        age: ${param.age }
        

        <jsp:useBean id="regMem" class="org.galaxy_OPEN.www.member.Register" />
        <jsp:setProperty name="regMem" property="*" />
        ${eShop:registerMember() }
    </body>
</html>
*******************
使用<jsp:useBean>标准动作,可以将这个JSP页面同一个JavaBean联系起来。而<jsp:setProperty>标准动作就是进行参数的传递。这里使用了"*"技术。与之相等价的语法实现是:
        <jsp:setProperty name="regMem" property="name" value="${param.name}">
        <jsp:setProperty name="regMem" property="age" value="${param.age}">
可知,如果一个bean类的参数和传入的名称、数目完全相同,那么就可以使用*技术,使其自动匹配。
这样,一个JSP就和一个Bean联系起来了。如果使用脚本,那么下面的一句就可以是
        <% regMem.registerMember(); %>
通过regMem对象调用其registerMember()方法。但是,我们并不希望使用脚本元素,所以事情没有这么简单。由于JSP是单纯的表现,除去脚本元素,JSP无法与JavaBean进行任何形式的通讯。这时,我们如果要访问bean的方法,唯一的途径就是将这个方法进行一下改写,然后把它包装成一个EL函数,通过EL访问。

EL函数要求bean方法必须是statuc的,因为EL需要通过类进行访问。于是,我们需要把registerMember()改写成静态的。
然后改动web.xml文档。由于EL函数必须具有一个前缀,所以我们需要在web.xml文档里定义一个自己的前缀。
*******************
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <taglib>
        <taglib-uri>
            http://www.galaxy_OPEN.org/eShop/eShop-taglib
        </taglib-uri>
        <taglib-location>/WEB-INF/eShop/eShopTags.tld</taglib-location>
    </taglib>

</web-app>
*******************
这里,我们定义了一个URI映射,由JSP指令
        <%@ taglib prefix="eShop" uri="http://www.galaxy_OPEN.org/eShop/eShop-taglib" %>
将我们自定义的前缀eShop指向这个URI:http://www.galaxy_OPEN.org/eShop/eShop-taglib  。同时,web.xml文档里把标签定义文件命名为eShopTags.tld,其位置在/WEB-INF/eShop/eShopTags.tld 。注意,所有的自定义TLD文件都必须在/WEB-INF/文件夹下。
然后在相应位置给出TLD文件定义:
*******************
<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <description>
        A taglib to define some EL accessible functions.
    </description>
    <tlib-version>1.0</tlib-version>
    <short-name>ELFunctionTaglib</short-name>
    <uri>/ELFunctionTagLibrary</uri>
    <function>
        <description>
            Exposes the registerMember() function from
            org.galaxy_OPEN.www.member.Register
        </description>
        <name>registerMember</name>
        <function-class>
            org.galaxy_OPEN.www.member.Register
        </function-class>
        <function-signature>
            void registerMember()
        </function-signature>
    </function>
</taglib>
*******************
这个文件中同时给出了标签调用名、相关联的类和函数声明。这是调用EL函数必须的。
在这之后,就可以像使用EL一样使用自定义的EL函数了:
        ${eShop:registerMember() }