jsp学习笔记

来源:互联网 发布:php strcmp漏洞 ctf 编辑:程序博客网 时间:2024/05/22 06:01

第一章,在页面中使用javaBeans组件

1.读取javaBean:

<jsp:getProperty name="useBeanName" property="name"/>

如果要用另一动作元素的值做另一个动作元素的属性值,不能直接用,必须用<jsp:attribute>元素.例:

<jsp:setProperty name="msg" property="cateory"/>

    <jsp:attribute name="value" trim="true">

          <jsp:getProperty name="myBean" property="myProperty"/>

    </jsp:attribute>

</jsp:setProperty>

jsp2.0可以用EL表达式读取javaBean,例:

<img src="images/${cartoon.fileName}">

二.设置Bean性质:

1.<jsp:setProperty name="msg" property="category"  value="thoughts">        <jsp:setProperty>还支持一个param属性如

<jsp:setProperty name="useinfo" property="useName" param="soemOterParam"/>

2.<c:set target="$msg}  property="category"  value="thoughts"/>

<c:set>属性

value 任何类型 必要,除非使用了体来提供值,此为所要设置的值 var String 可选,保存value的变量的名字,如果未指定,则必须使用tatget和property属性 scope String 可选,由var所指定的变量名的作用域 target JavaBea对象或java.util.Map 可选, property String 可选,对应设置的target,此为所指定的对象的性质名

如:<c:set var="p"  value="true"/>

注意:<jsp:getProperty>返回的值,直接置于模板文本中的EL表达式或者<c:out>总是会转换为一个String,而不论它表达的是何种Java类型.

第二章:使用定制标记库和JSP标准标记库

什么是定制标记库:为了扩展JSP标准动作无法做到的动作,JSP允许开发新的动作.可以开发为Java类,也可以开发为标记文件,类和标记文件的名字以及容器调用定制动作所需的其他信息均在一个名为TLD(标记库描述文件)的文件中指定定制标记库就是TLD以及对应一组相关定制动作全部文件的集合.

定制标记库的使用方法

1.安装:只需将对应库的JAR文件置于Web应用的WEB-INF/lib目录中.

2.声明:<%@ taglib prefix="ora" uri="orataglib"%>

第八章  处理输入和输出

这一章内容不少我准备按<<jsp设计>>的流程记笔记.呵呵

1.读取请求参数值

<c:out>动作用法:<c:out value=${param.userName}"/>

<c:out>属性

value 任何类型 必要.增加到响应的值 escapeXml Boolen 可选.true为特殊字符应当转换为字符实体.反之则反 Default 任何类型 可选.value为NULL时的值

<c:forEach>动作用法

<c:forEach items="${paramValues.food}   var="current">

    <c:out value="${current} />

<c:forEach/>

隐式paramValues变量是发送到页面的请求参数的集合,每个参数分别由一个值数组表示.

<c:forEach>属性

items Collection,Iterator.etc数组 可选.要循环处理的值的集合,如果为NULL,则不完成任何动作,如果未指定,则必须指定begin和end. var String 可选,保存当前元素值的变量的名 varStatus String 可选,保存迭代的状态(后面会讲到) begin Int 可选项,第一个索引 end Int 可选,最后一个索引 step Int 可选,每次迭代的索引增量

<c:forEach>功能很强大,有必要说明一个,呵呵,它会重复处理其动作体多次,如果一个变更表示某种形式的集合,<c:forEach>就可以对其加以处理,var属性指定了保存集合当前元素的变量的名,此变量只在动作体中可用.

如上例将<c:forEach>与paramValues隐式变量相结合,就可以实现迭代

由上面的属性表可知,<c:forEach>也可如下使用:

<c:forEach begin="1" end="2">

.............

</c:forEach>

 2.访问其他请求数据

通过隐式pageContext变量的request性质,在通过用EL表达式可以访问好多有用的性质 如:

<c:out value="${pageContext.request.method}''/>

<c:out value="${pageContext.request.protocol}"/> etc

<c:forEach item="${pageConext.request.cookies}" var="c">

   <c:out  value="c.name"/>

   <c:out value="c.value"/>

3.使用用Bean捕获参数值

bean一般做为数据的容器,但bean也可以用于捕获用户输入.

<jsp:useBean id="useinfo" class="com.ora....................">

    <jsp:setProperty name="useinfo" property="*"/>

</jsp:useBean>

<c:out  value="${useinfo.userName}"/>

 3.验证用户输入

简单的输入可以用标准JSTL动作来验证,复杂的用Bean来做,如果Servelt用于JSP时,可以用它来完成验证,

使用JSTL动作验证用户输入

关键页面代码:

<input type="hidden" name="submitted" value="true">

<c:if test="${param.submitted&&empty param.userName}">

......................

</c:if>

用submitted参数的原因是避免页面首次加载时显示错误信息,因为第一加载页面时,并未提交表单,submitted值自动设置为false

<c:if>属性

 test boolen 必要,计算为true或false var String 可选,保存boolen结果的变量的名 scope String 可选,变量作用域

empty操作符合并了NULL和空格,因为通常这两种情况总是做同样的处理,所以就把它合并处理,减少代码编写和出错的频率,它还可以用来测试各种类型的空集合

对于性别域的操作(见111P)

<c:if test="${param.submitted&&param.gender!='m'&&param.gender!='f'}">

.....................

</c:if>

<c:choose>

<c:when  test="${param.gender=='f'}">

...................

</c:when>

<c:otherwise>

........................

</c:otherwise>

</c:choose>

<c:choose>与它的嵌套动作<c:when> <c:otherwise>类似于switch语句

4.使用bean验证用户输入

在这里用了一个UserInfoBean,它包含一些验证代码,源代码如下:

import java.io.*;
import java.util.*;
import com.ora.jsp.util.*;

/**
 *这个类包含一些用户信息,它被用来说明如何用Bean捕获和验证用户输入
 */
public class UserInfoBean implements Serializable {
    // Validation constants
    private static String DATE_FORMAT_PATTERN = "yyyy-MM-dd";
    private static String[] GENDER_LIST = {"m", "f"};
    private static String[] FOOD_LIST = {"z", "p", "c"};
    private static int MIN_LUCKY_NUMBER = 1;
    private static int MAX_LUCKY_NUMBER = 100;

    // 属性
    private String birthDate;
    private String emailAddr;
    private String[] food;
    private String luckyNumber;
    private String gender;
    private String userName;

    /**
     * Returns the birthDate property value
     */
    public String getBirthDate() {
        return (birthDate == null ? "" : birthDate);
    }

    /**
     * Sets the birthDate property value
     */
    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }

    /**
     * birthDate property合法性验证
     *如果合法返回true,否则返回false
     */
    public boolean isBirthDateValid() {
        boolean isValid = false;
        if (birthDate != null &&
            StringFormat.isValidDate(birthDate, DATE_FORMAT_PATTERN)) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns the emailAddr property value
     */
    public String getEmailAddr() {
        return (emailAddr == null ? "" : emailAddr);
    }

    /**
     * Sets the emailAddr property value
     */
    public void setEmailAddr(String emailAddr) {
        this.emailAddr = emailAddr;
    }

    /**
     * Validates the emailAddr property
     *
     * @return true if the property is set to a valid value, false otherwise.
     */
    public boolean isEmailAddrValid() {
        boolean isValid = false;
        if (emailAddr != null &&
                 StringFormat.isValidEmailAddr(emailAddr)) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns the food property value
     */
    public String[] getFood() {
        return (food == null ? new String[0] : food);
    }

    /**
     * Sets the food property value
     */
    public void setFood(String[] food) {
        this.food = food;
    }

    /**
     * Validates the food property
     *
     * @return true if the property is set to a valid value, false otherwise.
     */
    public boolean isFoodValid() {
        boolean isValid = false;
        if (food == null ||
                 StringFormat.isValidString(food, FOOD_LIST, true)) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns true if the food property includes the marker for
     * pizza
     */
    public boolean isPizzaSelected() {
        return isFoodTypeSelected("z");
    }

    /**
     * Returns true if the food property includes the marker for
     * pasta
     */
    public boolean isPastaSelected() {
        return isFoodTypeSelected("p");
    }

    /**
     * Returns true if the food property includes the marker for
     * Chinese
     */
    public boolean isChineseSelected() {
        return isFoodTypeSelected("c");
    }

    /**
     * Returns the luckyNumber property value
     */
    public String getLuckyNumber() {
        return (luckyNumber == null ? "" : luckyNumber);
    }

    /**
     * Sets the luckyNumber property value
     */
    public void setLuckyNumber(String luckyNumber) {
        this.luckyNumber = luckyNumber;
    }

    /**
     * Validates the luckyNumber property
     *
     * @return true if the property is set to a valid value, false otherwise.
     */
    public boolean isLuckyNumberValid() {
        boolean isValid = false;
        if (luckyNumber != null &&
                 StringFormat.isValidInteger(luckyNumber, MIN_LUCKY_NUMBER,
                   MAX_LUCKY_NUMBER)) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns the gender property value
     */
    public String getGender() {
        return (gender == null ? "" : gender);
    }

    /**
     * Sets the gender property value
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     * Validates the gender property
     *
     * @return true if the property is set to a valid value, false otherwise.
     */
    public boolean isGenderValid() {
        boolean isValid = false;
        if (gender != null &&
                 StringFormat.isValidString(gender, GENDER_LIST, true)) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns the userName property value
     */
    public String getUserName() {
        return (userName == null ? "" : userName);
    }

    /**
     * Sets the userName property value
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * Validates the gender property
     *
     * @return true if the property is set to a valid value, false otherwise.
     */
    public boolean isUserNameValid() {
        boolean isValid = false;
        if (userName != null) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * Returns true if all property values have valid values
     * (they are only set if the value is valid).
     */
    public boolean isValid() {
        return isBirthDateValid() && isEmailAddrValid() &&
            isFoodValid() && isLuckyNumberValid() &&
            isGenderValid() && isUserNameValid();
    }

    /**
     * Returns true if the food property includes the specified food
     * type
     */
    private boolean isFoodTypeSelected(String foodType) {
        if (food == null) {
            return false;
        }
        boolean selected = false;
        for (int i = 0; i < food.length; i++) {
            if (food[i].equals(foodType)) {
                selected = true;
                break;
            }
        }
        return selected;
    }
}

import com.ora.jsp.util.*这个包中包含StringFormat类,它用做实际的验证工作.

在这里用Bean做验证和用JSTL的唯一有区别的代码是

<c:if test="${param.submitted&&userInfo.userNameValid==false}">

.......................

</c:if>

<input type="text" name="userName" value="<c:out value="${param.userName}"/>">

5.格式化HTML输出

<c:out>动作将所有存在问题的字符均加以转换,即转换为与之等价的所谓HTML的字符实体码,如对于单引号、双引号、小于号、大于号和&分别转换为字符实体码&#039  &#034   &It  &gt   &amp   如:

<c:out value=${param.userName}"/>对于诸如此类的语句时输入一个   whl,"seadragon"时,生成的HTML代码将是

whl,&#034seadragon&#034

如果不想用转换字符这个功能时,可以将escapeXml(一个<c:out>的属性)设置为false,在jsp2.0中可以用将EL表达式直接置于模板文本中实现同样的功能

另外在jsp2.0 jstl1.1中,也可以用fn:excapeXml()函数来实现转换特殊字符:

<input type="text" name="userName" value="${fn:excapceXml(userInfo.userName)}">

如果在相应标记库中还包含有定制动作,这种函数用于其中某个定制动作的属性值时,则可以忽略函数前缀(如:fn)。

一个知识点:如果EL表达式计算为NULL时,利用<c:out>动作可以指定一个默认的值以供使用,而使用fn:excapXml没有这个功能.

第四章 在JSP页面、请求和用户间共享数据

 将控制从一个页面传递到另一个页面:

<jsp:forward page="userinfoinput.jsp"/>      <jsp:forward>动作用停止一个页面的处理,并开始处理page属性所指定的页面,这称为目标页面,这种控制绝对不能返回到原页面。

目标页面可以访问请求所有相关信息,包括所有请求参数。将控制传递到另一个页面时,通过一个或多个嵌套<jsp:param>动作元素,还可以增加另外的请求参数:

<jsp:forward  page="userinfoinput.jsp">

   <jsp:param  name="msg"  value="inalid  email"/>

</jsp:forward>

如果请求中本来就有msg参数名,那么新值将加到该参数列表的第一个位置

page属性好不是以/开头,就是一个页面相对路径,如果以/开头就是一个上下文相对路径,即相对于应用web页面文件顶层目录的相对路径。还可以使用../foo/bar.jsp(详情见145P)

将数据从一个页面传递到另一个页面,这个用作用域技术来实现,有page.request.session application。

1.在页面间传递信息

//定义一个userInfoBean实例把它置于请求作用域中

<jsp:useBean id="userInfo"
      scope="request"
      class="com.ora.jsp.beans.userinfo.UserInfoBean"
    />

//表单action指向验证页面,而不是同一页面

<form action="userinfovalidate.jsp" method="post">

//验证页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<jsp:useBean id="userInfo"
  scope="request"
  class="com.ora.jsp.beans.userinfo.UserInfoBean">
  <jsp:setProperty name="userInfo" property="*" />
</jsp:useBean>

<c:choose>
  <c:when test="${userInfo.valid}">
    <jsp:forward page="userinfovalid.jsp" />
  </c:when>
  <c:otherwise>
    <jsp:forward page="userinfoinput.jsp" />
  </c:otherwise>
</c:choose>

典型的请求处理页面,不产生响应消息,只负责业务处理,并将控制传递给适当的目标页面。

2.共享会话和应用数据

2.1会话跟踪

两种方法:一为服务器可以利用各个响应来返回与当前用户有关的所有信息,并令浏览器将这些信息作为下一个请求的参数一起发回来,二为在服务器上保存客户状态,而只是发回一个标识符,此标识符可以用于找到保存在服务器上的状态信息。通常采用第二种。

信息将采用三种方法发送到浏览器:

作为一个cookies。

作为隐藏嵌套在一个HTML表单中。

编码于响应体的URL中,即URL重写

无论采用何种技术,会话数据总能通过会话作用域对JSP页面可用,保存在会话作用域中的信息在会话生命周期中对于同一个浏览器所发出的所有页面请求都是可用的

浏览器向特定应用中的某个JSP页面发出第一个请求时,会话开始,

通过下面的代码可以了解会话和应用作用域的区别

<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="
http://java.sun.com/jsp/jstl/core" %>

<html>
  <head>
    <title>Counter page</title>
  </head>
  <body bgcolor="white">

    <%-- Increment counters --%>
    <c:set var="sessionCounter" scope="session"
      value="${sessionCounter + 1}" />
    <c:set var="applCounter" scope="application"
      value="${applCounter + 1}" />

    <h1>Counter page</h1>

    This page has been visited <b>${sessionCounter}</b> times
    within the current session, and <b>${applCounter}</b> times
    by all users since the application was started.
  </body>
</html>

注意:如果变量用于一个算术操作,且未赋值,EL将把它解释为0

2.URL重写

<c:url>属性

value String 必要.绝对URL,或者是要编码的一个上下文相对路径或页面相对路径 context String 可选.如果资源并不当前应用的一部分,此为应用的上下文路径 var String 可选。保存编码的URL的变量的名

还有一个scope属性。

<c:url>动作也可以嵌套<c:param>动作所定义的按查询串进行编码

<c:url value="product.jsp">

   <c:param name="id" value="${product.id}/>

   <c:param name="customer" value="whl seadragon"/>

</c:url>

由该动作所创建的编码URL为

product.jsp;jsessionid=be8d.......?id=3&coustomer=whl+seadragon

特别当指定的URL有特殊字符时,必须自行进行替换。

<c:url>动作还会将上下文相对路径转换为服务器相对路径,以便能够用在HTML元素中,这说明,要从一个HTML元素指向位于应用顶层目录中的一个文件,所要做的就是使用<c:url>从而将其转换为浏览器可以理解的路径,例:

<img src="<c:url value="/images/logl.gif/>">

对于一个基于上下文路径/example安装的应用,处理此代码的结果为

<img src="/example/images/logl.gif">

用一个在线购物的例子说明上面的技术。

表示一个用户的购物车

import java.io.*;
import java.util.*;

/**
 * This class represents a shopping cart. It holds a list of products.
 *
public class CartBean implements Serializable {
    private Vector cart = new Vector();

    /**
     * Adds a product to the cart, if it's not already there.
     */
    public void setProduct(ProductBean product) {
        if (product != null && cart.indexOf(product) == -1) {
            cart.addElement(product);
        }
    }

    /**
     * Returns the product list.
     *
     * @return an Iterator of ProductBeans
     */
    public ProductBean[]  getProductList() {
 ProductBean[] products = null;
 synchronized(cart) {
     products = new ProductBean[cart.size()];
     cart.toArray(products);
 }
        return products;
    }

    /**
     * Returns the total price for all products in the cart
     *
     * @return the total price
     */
    public float getTotal() {
        float total = 0;
        Iterator prods = cart.iterator();
        while (prods.hasNext()) {
            ProductBean product = (ProductBean) prods.next();
            float price = product.getPrice();
            total += price;
        }
        return total;
    }
}

CatalogBean包含所有可购买的商品

import java.io.*;
import java.util.*;

/**
 * This class represents a product catalog. It holds a list of
 * products available for sale.
 * <p>
 * This is just a demo so the product list is hardcoded, created
 * at instantiation. A real version would get the information from
 * an external data source.
 */
public class CatalogBean implements Serializable {
    private Map catalog = new HashMap();

    /**
     * Constructor. Creates all ProductBean objects and adds them
     * to the catalog.
     */
    public CatalogBean() {
        ProductBean prod = new ProductBean();
        prod.setId("1000");
        prod.setName("JavaServer Pages");
        prod.setDescr("Learn how to develop a JSP based web application.");
        prod.setPrice(32.95f);
        catalog.put("1000", prod);

        prod = new ProductBean();
        prod.setId("2000");
        prod.setName("Java Servlet Programming");
        prod.setDescr("Learn how to develop a servlet based web application.");
        prod.setPrice(32.95f);
        catalog.put("2000", prod);

        prod = new ProductBean();
        prod.setId("3000");
        prod.setName("Java In a Nutshell");
        prod.setDescr("Learn the Java programming language.");
        prod.setPrice(32.95f);
        catalog.put("3000", prod);
    }

    /**
     * Returns all products as an Iterator, suitable for looping.

     */
    public ProductBean[] getProductList() {
 ProductBean[] products = new ProductBean[catalog.size()];
 catalog.values().toArray(products);
        return products;
    }

    /**
     * Returns a Map with all ProductBean instances, keyed by ID.
     */
    public Map getProductsById() {
        return catalog;
    }
}

目录中每一个商品都由一个ProductBean表示

import java.io.*;

/**
 * This class represents a product. It holds information about the
 * product's name, description and price. All setter methods have
 * package scope, since they are only used by the the CatalogBean.
public class ProductBean implements Serializable {
    private String id;
    private String name;
    private String descr;
    private float price;

    /**
     * Returns the product id
     */
    public String getId() {
        return id;
    }

    /**
     * Returns the product name.
     */
    public String getName() {
        return name;
    }

    /**
     * Returns the product description.
     */
    public String getDescr() {
        return descr;
    }

    /**
     * Returns the product price.
     */
    public float getPrice() {
        return price;
    }
   
    /**
     * Sets the product id.
     */
    void setId(String id) {
        this.id = id;
    }

    /**
     * Sets the product name.
     */
    void setName(String name) {
        this.name = name;
    }

    /**
     * Sets the product description.
     */
    void setDescr(String descr) {
        this.descr = descr;
    }

    /**
     * Sets the product price.
     */
    void setPrice(float price) {
        this.price = price;
    }
}

CatalogBean和ProductBean对象置于应用作用域,因为所有用户都共享同样的商品目录,而购物车是相对于用户的所以用于会话作用域中。

<jsp:useBean id="catalog" scope="application"
      class="com.ora.jsp.beans.shopping.CatalogBean"
    />

<c:forEach items="${catalog.productList}" var="product">
        <c:url var="productURL" value="product.jsp">
          <c:param name="id" value="${product.id}" />
        </c:url>
        <li>
          <a href="${productURL}">${fn:escapeXml(product.name)}</a>
      </c:forEach>
    </ul>

    <jsp:useBean
      id="cart" scope="session"
      class="com.ora.jsp.beans.shopping.CartBean"
    />

    <%-- Show the contents of the shopping cart, if any --%>
    <c:if test="${!empty cart.productList}">
      Your shopping cart contains the following items:
      <p>
      <table border=0>
        <c:forEach items="${cart.productList}" var="product">
          <tr>
            <td>${fn:escapeXml(product.name)}</td>
            <td>
              <fmt:formatNumber value="${product.price}"
                type="currency" />
            </td>
          </tr>
        </c:forEach>

        <tr><td colspan=2><hr></td></tr>
        <tr>
          <td><b>Total:</b></td>
          <td>
            <fmt:formatNumber value="${cart.total}"
              type="currency" />
          </td>
        </tr>
      </table>
    </c:if>
  </body>
</html>

需要说明的时<fmt:formatNumber>它用于把指定的value属性值格式化为由其他属性所定义的样式,如

<fmt:formatNumber value="${product.price}" type="currency"/> 具体的东西在后面再说

将请求参数用作索引

sp:useBean id="catalog" scope="application"
      class="com.ora.jsp.beans.shopping.CatalogBean"
    />

    <%-- Get the specified ProductBean from the catalog --%>
    <c:set var="product" value="${catalog.productsById[param.id]}" />

    <h1>${fn:escapeXml(product.name)}</h1>

    ${fn:escapeXml(product.descr)}

    <p>
    <c:url var="addtocartURL" value="addtocart.jsp">
      <c:param name="id" value="${product.id}" />
    </c:url>
 
    <a href="${addtocartURL}">
      Add this book to the shopping cart</a>

  </body>
</html>

需要说明的是<c:set var="product" value="${catalog.productsById[param.id]}" />,参数值可用于从一个集合中选择某个特定的元素。在此使用了EL[element_id]语法,在这个例子中catalogbean的productById性质类型为map,它通过标识符提供对各个元素的访问,此标识符称为主键。利用EL,可以采用两种方式来指定主键:

${myMap.mykey}

${myMap[mykey]}

第一种的主键只能是静态串。

如果指定为一个串直接量,而且主键名包含有点号,对于这种静态串必须使用以下语法:

${myMap['com.the.mykey']}

也可以用其所长${myList[1]}来访问加索引集合中的元素

向购物车增加一个商品的代码:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<jsp:useBean id="catalog" scope="application"
  class="com.ora.jsp.beans.shopping.CatalogBean"
/>

<%-- Get the specified ProductBean from the catalog --%>
<c:set var="product" value="${catalog.productsById[param.id]}" />

<jsp:useBean
  id="cart"
  scope="session"
  class="com.ora.jsp.beans.shopping.CartBean"
/>

<%-- Add the product to the cart --%>
<c:set target="${cart}" property="product" value="${product}" />

<c:redirect url="catalog.jsp" />
注意在上面使用<c:set>的原因,可以将target属性所标识的bean中由property属性指定的性质设置为value属性指定的EL表达式。

3.重定向和转发

第五章 将标记库开发为为标记文件

一个简单的标记文件:

<%@ tag body-content="empty" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl"%>

<jsp:useBean id="now" scope="application" class="java.util.Date" />
Copyright &copy; ${now.year + 1900} My Company

这个里最重要的是body-content属性,它可以设置为empty.scriptless(默认).tagdependent

如果取值为empty这个定制动作就不可以包含动作体,如果为scriptless时它可以包含除脚本元素外的任何JSP元素.如果是tagdependent时,它会把所有动作元素体处理为纯文本,

标记文件的部署方法为E:/Tomcat 5.0/webapps/ora/WEB-INF/tags/mytags/copyright.tag

默认地,标记文件所实现的定制动作的名字就是去掉扩展名.tag的文件名.

在JSP页面中使用由WEB应用结构中的标记文件所表示的标记库,就必须用如下的方法访问:

<%@ page contentType="text/html" %><%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %><html>  <body bgcolor="white">    Any old page content ...    <p>    <my:copyright/>  </body></html>
一个知识点:在Tomcat中标记文件最后被转换为JAVA使用
1.访问属性值

在标记文件中属性值必须用attribute指令来声明如:

<%@ tag body-content="empty" %>
<%@ attribute name="category" required="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<jsp:useBean id="mmb" class="com.ora.jsp.beans.motd.MixedMessageBean" />
<c:set target="${mmb}" property="category" value="${category}" />
${mmb.message}

在attribute指令中,required属性设置为true时,说明这个属性值网页设计人员是必须指定的,反之则反,它默认为false,rtexprvalue属性设置为true时,说明它的值可以是静态串,也可以是请求时属性值,false必须为一个静态串,它默认为true.

这个标记文件的使用方法:

<%@ page contentType="text/html" %>
<%@ taglib prefix="ora" uri="orataglib" %>

<html>
  <head>
    <title>Messages of the Day</title>
  </head>
  <body bgcolor="white">
    <h1>Messages of the Day</h1>
    <h2>Deep Thoughts - by Jack Handey</h2>
    <i>
      <ora:motd category="thoughts" />
    </i>

    <h2>Quotes From the Famous and the Unknown</h2>
    <i>
      <ora:motd category="quotes" />
    </i>
  </body>
</html>

通过在JSP页面中设置category属性值,标记文件会得到它设定的值,并运用到<c:set>动作中

下面是<jsp:useBean>指令中用到的Bean

package com.ora.jsp.beans.motd;

import java.util.*;

/**
 * This is an example of a bean with one writeable property for
 * selecting a message category and one readable property that
 * cycles through messages in the selected category.
 */
public class MixedMessageBean implements java.io.Serializable {
    private static int quoteIndex = -1;
    private static int thoughtIndex = -1;
    private List quotes;
    private List thoughts;
    private String category;

    /**
     * Contructor that initializes the data structures that hold
     * the messages.
     */
    public MixedMessageBean() {
        initMessageLists();
    }

    /**
     * Sets the message category, one of "quotes" or "thoughts".
     */
    public void setCategory(String category) {
 this.category = category;
    }

    /**
     * Returns a new message from the selected category every time
     * it's called, cycling through all available messages.
     */
    public String getMessage() {
 String msg = "No valid category selected";
 if ("quotes".equals(category)) {
     quoteIndex++;
     if (quoteIndex > quotes.size() - 1) {
  quoteIndex = 0;
     }
     msg = (String) quotes.get(quoteIndex);
 }
 else if ("thoughts".equals(category)) {
     thoughtIndex++;
     if (thoughtIndex > thoughts.size() - 1) {
  thoughtIndex = 0;
     }
     msg = (String) thoughts.get(thoughtIndex);
 }
 return msg;
    }

    /**
     * Creates and initalizes the data structures that hold the messages.
     */
    private void initMessageLists() {
        quotes = new ArrayList();
        quotes.add("The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents. --Nathaniel Borenstein");
 quotes.add("Any sufficiently advanced bug is indistinguishable from a feature. --Kulawiec");
 quotes.add("A computer lets you make mistakes faster than any other invention in human history, with the possible exception of handguns and tequila. --D.W. McArthur");
 quotes.add("The goal of Computer Science is to build something that will last at least until we've finished building it. --Unknown");

 thoughts = new ArrayList();
 thoughts.add("Maybe in order to understand mankind, we have to look at the word itself: 'Mankind'. Basically, it's made up of two separate words - 'mank' and 'ind'. What do these words mean ? It's a mystery, and that's why so is mankind.");
 thoughts.add("If you're robbing a bank and you're pants fall down, I think it's okay to laugh and to let the hostages laugh too, because, come on, life is funny.");
 thoughts.add("We tend to scoff at the beliefs of the ancients. But we can't scoff at them personally, to their faces, and this is what annoys me.");
 thoughts.add("If trees could scream, would we be so cavalier about cutting them down? We might, if they screamed all the time, for no good reason.");
    }
}

2.使用未声明属性

使用tag指令dynamic-attributes属性,它可以接受任何定制动作元素的未声明属性值,它被保存在一个Map中,在此集合中包含所有未声明的属性名和值,例:

<%@ tag body-content="empty" dynamic-attributes="dynattrs" %><%@ attribute name="caption" required="true" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><table  <c:forEach items="${dynattrs}" var="a">    ${a.key}="${a.value}"  </c:forEach>>  <caption>${caption}</caption>  <tr>    <th>Name</th>    <th>Value</th>  </tr>  <c:forEach items="${header}" var="h">    <tr>      <td>${h.key}</td>      <td>${h.value}</td>    </tr>  </c:forEach></table>
在上例中dynamic-attribute属性声明为一个dynattrs的变量来保存未声明属性,
由一个<c:forEach>循环处理此集合,并把每个属性的名和值增加到HTML<table>元素的属性列表
3.处理动作体
先说一下<jsp:body>属性:
varString可选,将体计算结果作为一个String加以保存的变量的名varReaderString可选.将体计算结果作为一个java.io.Reader加以保存的变量的名scopeString可选.变量作用域
它将计算定制动作元素的体,这说明体中的所有动作都要得到调用,
它们生成的输出与模板文本相混合,其结果保存在一个变量中,此变量的名由var属性所指定
还可以保存的由varReader指定的变量中,如果不指定,结果将增加到调用此定制动作的页面中.
(具体代码见177P)
4.处理片段属性
JSP片段是一组动态元素的可执行表示,也可以混合有模板文本,定制动作体实际上是一
个JSP片段的特例,
可以将命名片段作为定制动作属性来提供并由标记文件加以调用,并用<jsp:invoke>
<jsp:invoke>属性
下面用一个例子说明:
<%@ page contentType="text/html" %><%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><html>  <head>    <title>Even and Odd Rows</title>  </head>  <body bgcolor="white">    <h1>Even and Odd Rows</h1>    <table>      <my:forEvenAndOdd items="a,b,c,d,e">        <jsp:attribute name="even">          <c:set var="counter" value="${counter + 1}" />          <tr bgcolor="red"><td>${counter}: Even Row</td></tr>        </jsp:attribute>        <jsp:attribute name="odd">          <c:set var="counter" value="${counter + 1}" />          <tr bgcolor="blue"><td>${counter}: Odd Row</td></tr>        </jsp:attribute>      </my:forEvenAndOdd>    </table>  </body></html>
片段属性用<jsp:attribute>动作来定义,
下面为上面用到的tag
<%@ tag body-content="empty" %><%@ attribute name="items" rtexprvalue="true" required="true" %><%@ attribute name="even" fragment="true" required="true" %><%@ attribute name="odd" fragment="true" required="true" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:forEach items="${items}" varStatus="status">  <c:choose>    <c:when test="${status.count % 2 == 0}">      <jsp:invoke fragment="even" />    </c:when>    <c:otherwise>      <jsp:invoke fragment="odd" />    </c:otherwise>  </c:choose></c:forEach>注意:tag指令必须为empty,它只能包含<jsp:attribute>元素,另外,对于even和odd属性,
attribute的fragment属性均设置为true,否则容器只计算一次<jsp:attribute>
4.通过变量向调用页面提供数据
<%@ variable name-given="current" variable-class="java.lang.Object"scope="NESTED" %>
......
<c:forEach items="${items}" varStatus="status" var="current" >
<%@ variable>指令的scope属性可以取AT_BEGIN.AT_END.NESTED
用下面的方法使用这个变量:
<my:forEvenAndOdd2 items="a,b,c,d,e"> <jsp:attribute name="even">  <c:set var="counter" value="${counter + 1}" />  <tr bgcolor="red"><td>${counter}: Even Row: ${current}</td></tr>   </jsp:attribute>  <jsp:attribute name="odd"> <c:set var="counter" value="${counter + 1}" /> <tr bgcolor="blue"><td>${counter}: Odd Row: ${current}</td></tr>   /jsp:attribute>   </my:forEvenAndOdd2>
这个知识点比较难,具体看一下书的181P
5.将标记文件打包
标记文件的TLD
<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
    http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
  version="2.0">
  <tlib-version>1.0</tlib-version>
  <jsp-version>2.0</jsp-version>
  <short-name>mytags</short-name>
  <uri>mytaglib</uri>

  <tag-file>
    <name>copyright</name>
    <path>/WEB-INF/tags/mytags/copyright.tag</path>
  </tag-file>

  <tag-file>
    <name>forEvenAndOdd</name>
    <path>/WEB-INF/tags/mytags/forEvenAndOdd.tag</path>
  </tag-file>

<tag-file>
    <name>htmlFormat</name>
    <path>/WEB-INF/tags/mytags/htmlFormat.tag</path>
  </tag-file>

</taglib>

其中<uri>元素声明了库的默认URI

第六章 访问数据库

一般都是通过名为javax.sql.DataSourse的JDBC接口的一个实例不访问数据

JDBC驱动程序就是把JDBC API所定义的接口的实现翻译为特定引擎能理解的格式。

如果只需要访问一个数据库则可以使用部署描述文件中的一个上下文参数

<web-app>

......

<context-param>

   <param-name>

        javax.servelt.jsp.jstl.sql.dataSource

  </param-name>

  <param-value>

    jdbc.odbc.example,sun.jdbc.odbc.jdbcOdbcDriver.scott,tibet

  </param-value>

.......

<web-app>

如果需要访问多个数据库,可以使用<sql:setDataSource>例

<sql:setDataSource var="example" scope="appliation"

driver="sun.jdbc.odbc.JdbcOdbcDriver" url="jdbc.odbc.example"

user="scot" password="56"/>

对于需要数据源的动作,如果不是默认的数据源,还必须告诉它们究竟是用那一个

数据源:<sql:query var="empDbInfo" DataSource="example"

sql="SELECT * FROM Employee"/>

如果忽略了<sql:setDataSource> 中的var,数据源则用做指定作用域中的默认数据源,其实这就隐藏了上下文参数所定义的默认数据源,或者隐藏了“更大”作用域中另一个<sql:setDataSource>所设置的默认数据源,

1.在数据库中读取和存储信息

<sql:query>动作生成的结果是javax.servlet.jsp.jstl.sql.result类的一个实例,Result是一bean,它带有一些用于访问所有行及列值的性质,还有访问列名和行数的性质,它用属性var保存,属性startRow指定从结果中的那一行开始使用,maxRows指定结果中包含的最大行数,sql用来指定SQL语句也可用体来指定SQL语句,dataSource用来指定数据源,如果未指定使用默认数据源

在<sql:query>中可以嵌套使用<sql:param>用来指定SQL语句中的占位符,例<sql:query var="empDbInfo">
  SELECT * FROM Employee
    WHERE UserName = ?
  <sql:param value="${param.userName}" />
</sql:query>

也可以用下面的代码来实现上面的功能

<sql:query var="empDbInfo">
     SELECT * FROM Employee
    WHERE UserName =‘${param.userName} ’
</sql:query>

但是这样使用不安全,由于各种数据库引擎对SQL语句的单引号的处理方法不一样,这样容易受到脚本攻击。

<sql:updata>动作可以保存一Integer对象,以指定受此影响的行数,它保存在scope指定的作用域中,用var属性指定的变量名,

<sql:dateParam>用来设定日期值,如<sql:dateParam value="${parsedEmpDate} type="date"}/> type属性为可选,取值为date.time.timestamp(默认)

2.由查询结果生成HTML

关于Result

  rows java.util.SorteMap[] 只读 查询返回的行,作为一个不区分大小写的SortedMap实例数组返回, rowsByIndex Object[][] 只读 查询返回的行,作为一个复合的行数组返回,该数组的元素为列值数组 rowCount int 只读 结果中的行数 columnNames String 只读 列名 limitedByMaxRows boolen 只读 对于maxRows属性所指定的限制,如果因达到此限制而使结果存在截断.则为true

c:forEach items="${newEmpDbInfo.rows}" var="row">
        <c:forEach items="${row}" var="column">
          <tr>
            <td align=right>
              <b>${fn:escapeXml(column.key)}:</b>
            </td>
            <td>
              ${fn:escapeXml(column.value)}
            </td>
          </tr>
        </c:forEach>
      </c:forEach>

如果列名是已知的话就可以如下使用

<c:forEach items="${newEmpDbInto.rows}" var="row">

<tr>

<td>

  ${fn:escapeXml(row.FirstName)}

</td>

<td>

${fn:escapeXml(row.LastName)}

</td>

</tr>

</c:forEach>

3.基于部分信息搜索行

<sql:query var="empList" scope="request">
  SELECT * FROM Employee
    WHERE FirstName LIKE ?
      AND LastName LIKE ?
      AND Dept LIKE ?
    ORDER BY LastName
  <sql:param value="%${param.firstName}%" />
  <sql:param value="%${param.lastName}%" />
  <sql:param value="%${param.dept}%" />
</sql:query>

4.删除数据库信息

在这里没做新的技术,只是在代码中用一个比较好的技巧,我这里把部分主要代码写出来

<c:forEach items="${empList.rows}" var="row">
          <tr>
            <td>${fn:escapeXml(row.LastName)}</td>
            <td>${fn:escapeXml(row.FirstName)}</td>
            <td>${fn:escapeXml(row.Dept)}</td>
            <td>${fn:escapeXml(row.EmailAddr)}</td>
            <td>${fn:escapeXml(row.ModDate)}</td>
            <td>
              <form action="delete.jsp" method="post">
                <input type="hidden" name="userName"
                  value="${fn:escapeXml(row.UserName)}">
                <input type="hidden" name="firstName"
                  value="${fn:escapeXml(param.firstName)}">
                <input type="hidden" name="lastName"
                  value="${fn:escapeXml(param.lastName)}">
                <input type="hidden" name="dept"
                  value="${fn:escapeXml(param.dept)}">
                <input type="submit" value="Delete">
              </form>
            </td>
          </tr>
        </c:forEach>

它通过在调用一个delete.jsp页面的按钮来完成删除,同时要给delete.jsp发送一些隐藏域,delete.jsp代码如下:

<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><sql:update>  DELETE FROM Employee     WHERE UserName = ?  <sql:param value="${param.userName}" /></sql:update><c:redirect url="find.jsp">  <c:param name="firstName" value="${param.firstName}" />  <c:param name="lastName" value="${param.lastName}" />  <c:param name="dept" value="${param.dept}" /></c:redirect>
5.在多个页面上显示数据库数据
可以设置一个上下文参数,来限制应用中所有JSTL<sql:query>动作所返回的行数:
<web-app>
.....
<context-param>
  <param-name>
    javax.servlet.jsp.jstl.sql.maxRows
  </param-name>
  <param-value>
    100
  </param-value>
  </context-param>
</web-app>
也可以用<sql:query>动作的maxRows属性来覆盖对应某个动作的最大行数.
6.不采用bean验证复杂的输入
这一小节里唯一需要掌握的是:
<c:catch var="invalidDate">  <fmt:parseDate value="${param.empDate}" pattern="yyyy-MM-dd"    var="ignore" /></c:catch><c:choose>  <c:when test="${empty param.empDate}">    <c:set var="empDateError" scope="request"      value="Employment Date missing" />    <c:set var="isValid" value="false" />  </c:when>  <c:when test="${invalidDate != null}">    <c:set var="empDateError" scope="request"      value="Invalid Employment Date" />    <c:set var="isValid" value="false" />  </c:when></c:choose>
用<fmt:parseDate>可以处理日期串,具体看国际化那一章
6.使用事务
事务是一个原子操作,也就是说如果一条语句失败,那么事务中的
所有语句都失败,反之则反,如果成功,则称为提交(committing)事务
,如果失败则称为回滚(rolling back)
<sql:transaction>属性有dataSource__要使用的数据源.
还有isolation属性,可取值为read_committed.
read_uncommitted.repeatable_read.serializable