天天看點

JavaWeb中級

Servlet的分類

  1. 簡單Servlet
  2. 過濾Servlet
  3. 監聽Servlet

Servlet的生命周期

容器決定Servlet的生命周期。包括加載、初始化、服務、銷毀、解除安裝5個部分。

JavaWeb中級

換一種方式了解Servlet的生命周期

JavaWeb中級

對于

HttpServlet

類而言,裡面的

services

方法主要完成的功能是區分get請求還是post請求,進而分别對于

doGet

doPost

方法。但是子類一旦覆寫了

services

方法,則沒有了區分能力——

doGet

doPost

方法都将失效。——實際上這種做法更像一種設計的模闆操作,因為隻需要按照它的要求将方法覆寫了就可以完成相應的功能——模闆設計模式。

使用Servlet取得其他内置對象

取得HttpSession執行個體

通過

HttpServletRequest

接口提供的以下方法完成:

HttpSession getSession()
HttpSession getSession(boolean create)
           
public class HttpServletSession extends HttpServlet {

    public HttpServletSession() {
        super();
    }

    public void destroy() {
        super.destroy(); // Just puts "destroy" string in log
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpSession session = request.getSession(); // 取得session
        System.out.println("sessionID:" + session.getId());
        session.setAttribute("username", "劉洋"); // 設定session屬性
        System.out.println("屬性内容:" + session.getAttribute("username"));
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
           

web.xml

<servlet>
  <servlet-name>sessiondemo</servlet-name>
  <servlet-class>servlet.HttpServletSession</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>sessiondemo</servlet-name>
  <url-pattern>/HttpSessionDemoServlet</url-pattern>
</servlet-mapping>
           
JavaWeb中級

取得application對象的執行個體

HttpServlet

的父類

GenericServlet

提供了

public ServletContext getServletContext()

方法可以取得

ServletContext

接口的執行個體。

public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

  ServletContext app = super.getServletContext();
  System.out.println("項目的真實路徑:" + app.getRealPath("/"));
}
           

表達式語言

Jsp2.0之後提供了表達式語言(Expression Language,簡稱EL),可以通路标志位page(pageContext)、request、session、application中的屬性内容,這樣就可以在jsp頁面中有很少的script代碼,可以避免null的問題文法如下:

${屬性名稱}
           

下面這個例子說明了使用EL和不使用EL的差別:

<%
    request.setAttribute("username", "陶偉華");
%>

 <!-- 使用表達式語言輸出 -->
 ${username}

 <!-- 不使用EL表達式,需要判斷非空 -->
<%
    if(request.getAttribute("username")!=null){
%>
    <%=request.getAttribute("username") %>
<%  
    }
%>
           

表達式語言的内置對象

表達式内置對象 說明
pageContext javax.servlet.jsp.pageContext對象
pageScope page屬性範圍查找輸出屬性
requestScope request屬性範圍查找輸出屬性
sessionScope session屬性範圍查找輸出屬性
applicationScope application屬性範圍查找輸出屬性
param 接收傳遞到本頁面的參數
paramValues 接收傳遞到本頁面的一組參數
header 取得一個頭資訊資料
headValues 取得一組頭資訊資料
cookie 取得cookie中的資料
initParam 取得配置的初始化參數

EL表達式的查找範圍:

<%
//  pageContext.setAttribute("info", "page屬性範圍");
//  request.setAttribute("info", "request屬性範圍");
    session.setAttribute("info", "session屬性範圍");
    application.setAttribute("info", "application屬性範圍");
%>

 <!-- EL表達式查找範圍示例 -->
 ${info}
           

如果在不同的屬性範圍之内儲存了同名屬性,則EL的查找範圍是:page–>request–>session–>application。這個時候就可以使用EL内置對象幫助我們找到特定屬性範圍之内的屬性。(PS:實際上在開發中應該盡量避免同名屬性)。

<%
    pageContext.setAttribute("info", "page屬性範圍");
    request.setAttribute("info", "request屬性範圍");
    session.setAttribute("info", "session屬性範圍");
    application.setAttribute("info", "application屬性範圍");
%>

 <!-- EL表達式查找特定屬性範圍的屬性 -->
 page屬性内容:${pageScope.info }<br />
 request屬性内容:${requestScope.info }<br />
 session屬性内容:${sessionScope.info }<br />
 application屬性内容:${applicationScope.info }<br />
           

EL的内置對象中有pageContext。之前在JSP中我們可以通過pageContext取得request、session、application的JSP内置對象。我們可以直接使用EL直接完成内置對象的方法調用(實際上依靠的是java的反射機制)。

IP位址:${pageContext.request.remoteAddr}<br />
 session ID:${pageContext.session.id}<br />
           

接收參數

表單和URL都可以傳遞參數。在EL中可以使用

param

對象完成。

通過内置對象擷取URL參數:<%=request.getParameter("ref") %><br/>
EL表達式擷取URL參數:${param.ref}
           
JavaWeb中級

接收一組參數

paramValues

<form action="get_param.jsp">
    請選擇興趣:
    <input type="checkbox" name="inst" value="遊泳">遊泳
    <input type="checkbox" name="inst" value="看電視">看電視
    <input type="checkbox" name="inst" value="看書">看書
    <input type="checkbox" name="inst" value="打遊戲">打遊戲
    <input type="checkbox" name="inst" value="打籃球">打籃球
    <input type="submit" value="送出">
  </form>
使用EL表達式擷取一組參數:<br />
第一個參數:${paramValues.inst[0] }<br />
第二個參數:${paramValues.inst[1] }<br />
第三個參數:${paramValues.inst[2] }<br />
第四個參數:${paramValues.inst[3] }<br />
第五個參數:${paramValues.inst[4] }
           
JavaWeb中級

但是在實際的開發過程中使用最多的還是

param

,接收多個參數的

params

并不常用。因為從MVC設計模式上講,所有的參數接收都應該交給Servlet。

EL表達式中的集合操作

List和Set接口的本質差別是List接口對Collection接口進行了擴充,而Set接口并沒有對Collection接口進行擴充。

輸出Collection

<%
    List<String> all = new ArrayList<String>();
    all.add("Tom");
    all.add("Jack");
    all.add("Thinking in java");
    all.add("Effective java");

    request.setAttribute("info", all); // 集合儲存在request範圍
 %>

 <!-- 輸出集合中的内容 -->
 第一個内容:${ info[0]}<br />
 第二個内容:${ info[1]}<br />
 第三個内容:${ info[2]}<br />
 第四個内容:${ info[3]}<br />
 第五個内容:${ info[4]}<br />
           
JavaWeb中級

輸出Map

<%
    Map<String,String> all = new HashMap<String,String>();
    all.put("001", "Tomcat");
    all.put("002", "EJB");
    all.put("003", "先序周遊");
    all.put("004", "SSH");

    request.setAttribute("info", all); // 集合儲存在request範圍
 %>

 <!-- 輸出Map中的内容 -->
key為001的内容:${ info["001"]}<br />
key為002的内容:${ info["002"]}<br />
key為003的内容:${ info["003"]}<br />
key為004的内容:${ info["004"]}<br />
key為005的内容:${ info["005"]}<br />
           
JavaWeb中級

在MVC中應用EL

在之前講解MVC的時候一直都有DAO的存在,而且所有的對象都是儲存在VO之中。如果将一個VO對象傳遞到JSP的話,則必須在JSP頁面中導入VO包,如果現在使用了EL表達式的話,這個包的導入就沒有任何意義了。

vo

package org.gpf.vo;

public class Dept {

    private int deptno;
    private String dname;
    private String loc;

    public int getDeptno() {
        return deptno;
    }

    public void setDeptno(int deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc
                + "]";
    }

}
           

Servlet:ELServlet

public class ELServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        Dept dept = new Dept();
        dept.setDname("學工部");
        dept.setDeptno();
        dept.setLoc("武昌區");
        request.setAttribute("deptinfo", dept);

        request.getRequestDispatcher("dept_info.jsp").forward(request, response);
    }

}
           

JSP頁面deptinfo.jsp

部門編号:${deptinfo.deptno }<br />
 部門名稱:${deptinfo.dname }<br />
 部門位置:${deptinfo.loc }<br />
 ${deptinfo }<br />
           
JavaWeb中級

以上的JSP頁面沒有調用任何的getter就進行了對象的輸出,則肯定應用到了反射機制。

使用MVC傳遞集合

ELServlet

public class ELServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        List<Dept> all = new ArrayList<Dept>();
        Dept dept = new Dept();
        dept.setDname("學工部");
        dept.setDeptno();
        dept.setLoc("武昌區");
        all.add(dept);
        dept = new Dept();
        dept.setDeptno();
        dept.setDname("衛生部");
        dept.setLoc("黃浦區");
        all.add(dept);

        request.setAttribute("allDept", all);
        request.getRequestDispatcher("dept_info.jsp").forward(request, response);
    }

}
           

JSP頁面:dept_info.jsp

<table border="1">
<%
    // 由于JSP頁面中隻允許導入java.util包,是以不使用泛型
    List all = (List)request.getAttribute("allDept");
    if (all != null) {
        Iterator iterator = all.iterator();
%>
            <tr>
                <th>部門編号</th>
                <th>部門名稱</th>
                <th>部門位置</th>
                <th>部門對象</th>
            </tr>
<%
        while(iterator.hasNext()){
            // 将疊代的對象儲存在本頁面(pageContext)屬性範圍當中
            pageContext.setAttribute("dept", iterator.next());
%>
            <tr>
                <!-- 用取得儲存在page屬性範圍之内的對象 -->
                <td>${dept.deptno}</td>
                <td>${dept.dname}</td>
                <td>${dept.loc}</td>
                <td>${dept}</td>
            </tr>
<%          
        }
    }
%>
    </table>
           
JavaWeb中級

EL運算符

表達式給我們進行了自動的類型轉換,是以我們在使用的時候根本不需要考慮類型問題。

<%
    pageContext.setAttribute("num1", );
    pageContext.setAttribute("num2", );

    pageContext.setAttribute("flagA", true);
    pageContext.setAttribute("flagB", false);
 %>
 num1 = ${num1 },num2 = ${num2 }
 <h2>EL數學運算</h2>
 加:${num1+num2 }<br /> 
 減:${num1-num2 }<br /> 
 乘:${num1*num2 }<br /> 
 除:${num1/num2 }和${num1 div num2 }<br /> 
 取模:${num1%num2 }和${num1 mod num2 }<br />

 <h2>EL關系運算符</h2>
相等判斷:${num1==num2 }和${num1 eq num2 }<br />
不等判斷:${num1!=num2 }和${num1 ne num2 }<br />
大于判斷:${num1>num2 }和${num1 gt num2 }<br />
小于判斷:${num1<num2 }和${num1 lt num2 }<br />
大于等于判斷:${num1>=num2 }和${num1 ge num2 }<br />
小于等于判斷:${num1<=num2 }和${num1 le num2 }<br />

 <h2>EL邏輯運算符</h2>
flagA = ${flagA },flagB = ${flagB } <br />
 與:${flagA&&flagB }和${flagA and FlagB }<br />
 或:${flagA||flagB }和${flagA or FlagB }<br />
 非:${!flagA }和${not flagA }<br />
           

Tomcat資料源

在代碼中使用資料源可以提升程式的運作效率。

傳統JDBC操作分為以下幾個步驟:

加載DB驅動—>獲得DB連接配接—>對DB進行CRUD操作—>關閉DB連接配接。

JavaWeb中級

對于不同的使用者隻有操作的不同,而加載DB驅動、連接配接DB、關閉DB連接配接則是一個重複的操作。産生了性能瓶頸——如果不關閉資料庫,使用者連接配接的時候直接取出一個連接配接這樣就可以省略1、2、4的3個步驟了。要進行這樣的操作需要有3個量度:最小維持的資料庫連接配接數、最大允許打開的連接配接數、等待時間。Tomcat4.1之後就支援這種操作了,這種操作稱為資料庫連接配接池(存放的資料庫連接配接)。

JavaWeb中級

在WEB容器中,資料庫的連接配接池都是通過資料源

javax.sql.DataSource

通路的。即可以通過

javax.sql.DataSource

類取得一個Connection對象,但是如果需要得到Connection對象需要使用JNDI進行查找。

JNDI。Java Naming and Directory Interface,java命名及查找目錄接口,主要的功能是進行查找對象的。

下面是一個MySQL的Tomcat資料源配置:

WebRoot/META-INF/context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context reloadable="true">
 <!-- 配置Tomcat資料源,name資料源名稱,也是JNDI查找名稱;auth表示容器負責資源的連接配接,還有一個值是application(通過應用程式自己進行控制);type對象,資料源上每一個綁定的都是DataSource;maxActive最大連接配接數;minIdle最少維持數量;maxWait最長等待時間 -->
 <Resource name="jdbc/mldn"  
   auth="Container"
   type="javax.sql.DataSource" 
   maxActive="100"   
   maxIdle="30"    
   maxWait="10000"   
   username="root"   
   password="mysqladmin"   
   driverClassName="org.gjt.mm.mysql.Driver" 
   url="jdbc:mysql://localhost:3306/mldn"/>
</Context>
           

如果是Oracle資料庫隻需要更改相應的驅動程式和連接配接位址即可。

WebRoot/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 

<!-- 配置資源引用 -->
<resource-ref>
    <res-ref-name>jdbc/mldn</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>  

</web-app>
           

查找資料源

資料源的操作使用的是JNDI的方式進行查找的,如果想要使用資料源取得資料庫的連接配接的話,則必須按照以下的步驟進行:

  1. 初始化名稱查找上下文(javax.naming包)。

    Context ctx = new InitialContext()

    ;
  2. 通過名稱查找

    DataSource

    對象。

    DataSource ds = (DataSource)ctx.lookup(JNDI名稱)

  3. 通過

    DataSource

    取得一個資料庫連接配接。

    Connection conn = ds.getConnection();

<%
    String DSNAME = "java:comp/env/jdbc/mldn";      // 名稱
    Context ctx = new InitialContext();
    DataSource ds = (DataSource)ctx.lookup(DSNAME); 
    Connection conn = ds.getConnection();           // 從連接配接池中取得連接配接
 %>
 <%=conn %>
 <%
    conn.close(); // 将連接配接放回到池之中
 %>
           
JavaWeb中級

在基于Tomcat的web應用程式中就可以擺脫在JDBC中使用Class.forName取得資料庫連接配接了。

JSP标簽程式設計

一個簡單的标簽

标簽程式設計是為了盡量減少頁面中的script代碼。使用者定義一個标簽隻需要繼承

javax.servlet.jsp.tagext.TagSupport

類。如果要定義的标簽内沒有标簽體,則直接覆寫TagSupport類中的

doStartTag()

方法即可。一個jsp标簽需要有3部分:标簽檔案(.java),标簽庫描述檔案(.tld),标簽顯示檔案(.jsp),如果需要的話還可以在web.xml中注冊該标簽的uri和路徑。

一、定義标簽支援類HelloTag.java

public class HelloTag extends TagSupport {

    /**
     * 标簽開始
     */
    @Override
    public int doStartTag() throws JspException {

        JspWriter out = pageContext.getOut();
        try {
            out.write("Hello World!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return TagSupport.SKIP_BODY;
    }
}
           

二、定義标簽庫描述檔案WEB-INF/hellotab.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            <!-- 标簽庫版本 -->
    <short-name>firsttag</short-name>           <!-- 标簽庫在TLD中的描述名稱 -->
    <tag>
        <name>hello</name>                      <!-- 标簽庫在JSP中的使用名稱 -->
        <tag-class>org.gpf.tag.HelloTag</tag-class>
        <body-content>empty</body-content>      <!-- 标簽體内容為空 -->
    </tag>
</taglib>
           

三、編寫jsp頁面并使用标簽hellotag.jsp

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="/WEB-INF/hellotag.tld"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
    <!-- 使用标簽 -->
    <mytag:hello />
  </body>
</html>
           

或者在web.xml中進行以下配置:

<!-- 配置jsp标簽 -->    
<jsp-config>
    <taglib>
        <taglib-uri>hello_tag</taglib-uri>
        <taglib-location>/WEB-INF/hellotag.tld</taglib-location>
    </taglib>
</jsp-config>
           

在jsp頁面中這樣使用:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="hello_tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
    <!-- 使用标簽 -->
    <mytag:hello />
  </body>
</html>
           

定義有屬性的标簽

定義一個可以完成日期格式化的操作,希望使用者可以自己輸入日期格式化的模闆,根據此模闆最終完成目前日期的顯示功能

1.建立一個标簽的支援類DateTag.java

public class DateTag extends TagSupport {

    private String format; // 此屬性通過setter和getter完成

    public String getFormat() {
        return format;
    }

    public void setFormat(String format) {
        this.format = format;
    }

    @Override
    public int doStartTag() throws JspException {

        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            // 進行格式化的日期輸出
            super.pageContext.getOut().write(sdf.format(new Date()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return TagSupport.SKIP_BODY;
    }
}
           

2.定義标簽庫描述檔案(DateTag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            <!-- 标簽庫版本 -->
    <short-name>date</short-name>               <!-- 标簽庫在TLD中的描述名稱 -->
    <tag>
        <name>date</name>                       <!-- 标簽庫在JSP中的使用名稱 -->
        <tag-class>org.gpf.tag.DateTag</tag-class>
        <body-content>empty</body-content>      <!-- 标簽體内容為空 -->
        <attribute>
            <name>format</name>                 <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
    </tag>
</taglib>
           

3.在web.xml中定義此标簽庫

<jsp-config>
    <taglib>
        <taglib-uri>hello_tag</taglib-uri>
        <taglib-location>/WEB-INF/hellotag.tld</taglib-location>
    </taglib>
    <taglib>
        <taglib-uri>date_tag</taglib-uri>
        <taglib-location>/WEB-INF/DateTag.tld</taglib-location>
    </taglib>
</jsp-config>
           

4.在jsp頁面中使用以上jsp标簽

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="date_tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
    <!-- 使用标簽 -->
    <mytag:date format="yyyy-MM-dd HH:mm:ss.SSS"/>
  </body>
</html>
           
JavaWeb中級

TagSupport類

該類是jsp标簽程式設計的核心類,其定義如下:

public class TagSupport Object implements IterationTag, Serializable

。常用的屬性和方法:

JavaWeb中級

doStartTag()

标簽開始的時候執行,此方法有2種傳回值:

  1. SKIP_BODY:忽略标簽體的内容,将執行權轉交給

    doEnd()

    方法。
  2. EVAL_BODY_INCLUDE:執行标簽體的内容。

doAfterBody()

此方法是IterationTag接口和Tag接口的差別所在,本方法用來重複執行标簽體的内容,有2種傳回值:

  1. SKIP_BODY:忽略标簽體的内容,将執行權轉交給

    doEnd()

    方法。
  2. EVAL_BODY_AGAIN:重複執行标簽體的内容,會重複調用

    doAfter()

    方法,一直循環執行下去,直到

    doAfterBody()

    方法傳回SKIP_BODY為止。

doEndTag()

标簽結束時執行,兩種傳回值:

  1. SKIP_PAGE:jsp頁面應該立即停止執行,并将所有輸出回傳到浏覽器。
  2. EVAL_PAGE:表示JSP可以正常運作完畢。

release()

将标簽處理類所産生的或者是獲得的資源全部釋放,并等待使用者下次繼續使用。

Tag接口的執行流程

JavaWeb中級

IterationTag接口的執行流程

JavaWeb中級

标簽支援類AttributeTag.java

public class AttributeTag extends TagSupport {

    private String name;    // 接收屬性名稱
    private String scope;   // 接收屬性範圍

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    /**
     * 判斷屬性是否存在
     */
    @Override
    public int doStartTag() throws JspException {

        Object value = null;
        if ("page".equals(scope))           // 是否是page範圍
            value = super.pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
        if ("request".equals(scope))        // 是否是request範圍
            value = super.pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
        if ("session".equals(scope))        // 是否是session範圍
            value = super.pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
        if ("application".equals(scope))    // 是否是application範圍
            value = super.pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);

        if (value == null) 
            return TagSupport.SKIP_BODY;            // 沒有屬性不執行标簽體
        else
            return TagSupport.EVAL_BODY_INCLUDE;    // 執行标簽體
    }
}
           

标簽描述檔案AttributeTag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            <!-- 标簽庫版本 -->
    <short-name>tag</short-name>                <!-- 标簽庫在TLD中的描述名稱 -->
    <tag>
        <name>parent</name>                     <!-- 标簽庫在JSP中的使用名稱 -->
        <tag-class>org.gpf.tag.AttributeTag</tag-class>
        <body-content>JSP</body-content>        <!-- 标簽體内容為空代碼 -->
        <attribute>
            <name>name</name>                   <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
        <attribute>
            <name>scope</name>                  <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
    </tag>
</taglib>
           

在web.xml中定義以上tld檔案:

<taglib>
    <taglib-uri>tag</taglib-uri>
    <taglib-location>/WEB-INF/DateTag.tld</taglib-location>
</taglib>
           

在jsp頁面中使用以上标簽

<%
  String scope = "session"; // 假設是session範圍
  session.setAttribute("username", "張小凡");
%>
<mytag:parent name="username" scope="<%=scope %>">
    <%=scope %>範圍存在屬性,内容是:“${sessionScope.username }”
</mytag:parent>

<mytag:parent name="user" scope="<%=scope %>">
    request範圍存在屬性,内容是:“${requestScope.user }”
</mytag:parent>
           
JavaWeb中級

開發疊代标簽

标簽處理類MyTag.java

public class MyTag extends TagSupport {

    private String name;    // 接收屬性名稱
    private String scope;   // 接收屬性範圍
    private String id;      // 用于儲存集合中的每一個元素

    private Iterator<?>iterator = null;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * 判斷屬性是否存在
     */
    @Override
    public int doStartTag() throws JspException {

        Object value = null;
        if ("page".equals(scope))           // 是否是page範圍
            value = super.pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
        if ("request".equals(scope))        // 是否是request範圍
            value = super.pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
        if ("session".equals(scope))        // 是否是session範圍
            value = super.pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
        if ("application".equals(scope))    // 是否是application範圍
            value = super.pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);

        if (value != null && value instanceof List<?>){
            iterator = ((List<?>)value).iterator();
            if (iterator.hasNext()) {
                super.pageContext.setAttribute(id, iterator.next());    // 将屬性儲存在page屬性範圍中
                return TagSupport.EVAL_BODY_INCLUDE;
            }else {
                return TagSupport.SKIP_BODY;
            }
        }else {
            return TagSupport.SKIP_BODY;
        }
    }

    @Override
    public int doAfterBody() throws JspException {

        if (iterator.hasNext()) {
            super.pageContext.setAttribute(id, iterator.next());    // 将屬性儲存在page屬性範圍中
            return TagSupport.EVAL_BODY_AGAIN;                      // 反複執行doAfterBody()方法
        }else {
            return TagSupport.SKIP_BODY;
        }
    }
}
           

标簽描述檔案MyTag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            <!-- 标簽庫版本 -->
    <short-name>tag</short-name>                <!-- 标簽庫在TLD中的描述名稱 -->
    <tag>
        <name>iterator</name>                       <!-- 标簽庫在JSP中的使用名稱 -->
        <tag-class>org.gpf.tag.MyTag</tag-class>
        <body-content>JSP</body-content>        <!-- 标簽體内容為空代碼 -->
        <attribute>
            <name>name</name>                   <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
        <attribute>
            <name>scope</name>                  <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
        <attribute>
            <name>id</name>                 <!-- 設定屬性 -->
            <required>true</required>           <!-- 此屬性是必須的 -->
            <rtexprvalue>true</rtexprvalue>     <!-- 支援表達式輸出 -->
        </attribute>
    </tag>
</taglib>
           

在web.xml中配置标簽

<jsp-config>
    <taglib>
        <taglib-uri>tag</taglib-uri>
        <taglib-location>/WEB-INF/MyTag.tld</taglib-location>
    </taglib>
</jsp-config>
           

在jsp頁面中使用标簽

<%
    // 此代碼僅僅是測試,實際的操作應該交給Servlet完成
    List<String> all = new ArrayList<String>();
    all.add("Thinking in Java");
    all.add("鋒利的Jquery");
    all.add("陸雪琪");
    request.setAttribute("all", all);
   %>

    <mytag:iterator id="content" name="all" scope="request">
        内容:${content }<br />
    </mytag:iterator>
           
JavaWeb中級

BodyTagSupport類

該類是TagSupport類的子類。繼承BodyTagSupport實作的标簽可以直接處理标簽體内容的資料,該類的定義如下:

JavaWeb中級

BodyContent類

BodyTagSupport

類中定義了一個

bodyContent

的受保護的屬性,而

bodyContent

BodyContent

類的對象,此類定義如下:

可以發現

BodyContent

類是

JspWriter

類的子類,可以直接列印和輸出基本類型和對象值,但是

BodyContent

類和

JspWriter

類的差別在于:

BodyContent

的任何寫入值都不會自動向頁面輸出。

JavaWeb中級
JavaWeb中級

TagExtraInfo類和VariableInfo類

在本程式中定義了一個

simple

的屬性名稱,但是這個

simple

卻可以像對象一樣,可以直接在scriptlet中通路。而如果使用者自定義的标簽也需要實作同樣的效果,就需要通過

TagExtraInfo

類和

VariableInfo

類來完成。

<jsp:useBean id="simple" scope="page" class="org.gpf.bean.SimpleBean" />

<%
    simple.setName("雨師妾"); // 設定name屬性
    simple.setAge();       //  設定age屬性
%>
           

TagExtraInfo

有一個方法:

public VariableInfo[] getVariableInfo(TagData data)

該方法可以擷取一組

VariableInfo

對象。

VariableInfo

類的主要方法:

常量及方法 描述
public static final int AT_BEGIN 變量的作用範圍從開始标簽一直到jsp頁面結束
public static final int AT_END 變量範圍從結束标簽一直到jsp頁面結束
public static final int NESTED 變量的範圍從開始标簽到結束标簽
public VariableInfo(String varName,String className,boolean declare,int scope) 構造,執行個體化VariableInfo對象

範例:修改上面的疊代标簽。

修改之前的疊代标簽

public class MyTag extends BodyTagSupport {

    private String name;    // 接收屬性名稱
    private String scope;   // 接收屬性範圍
    private String id;      // 用于儲存集合中的每一個元素

    private Iterator<?>iterator = null;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * 判斷屬性是否存在
     */
    @Override
    public int doStartTag() throws JspException {

        Object value = null;
        if ("page".equals(scope))           // 是否是page範圍
            value = super.pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
        if ("request".equals(scope))        // 是否是request範圍
            value = super.pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
        if ("session".equals(scope))        // 是否是session範圍
            value = super.pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
        if ("application".equals(scope))    // 是否是application範圍
            value = super.pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);

        if (value != null && value instanceof List<?>){
            iterator = ((List<?>)value).iterator();
            if (iterator.hasNext()) {
                super.pageContext.setAttribute(id, iterator.next());    // 将屬性儲存在page屬性範圍中
                return BodyTagSupport.EVAL_BODY_BUFFERED;               // 執行标簽體的操作
            }else {
                return BodyTagSupport.SKIP_BODY;
            }
        }else {
            return BodyTagSupport.SKIP_BODY;
        }
    }

    @Override
    public int doAfterBody() throws JspException {

        if (iterator.hasNext()) {
            super.pageContext.setAttribute(id, iterator.next());    // 将屬性儲存在page屬性範圍中
            return BodyTagSupport.EVAL_BODY_BUFFERED;               // 執行标簽體的操作
        }else {
            return BodyTagSupport.SKIP_BODY;
        }
    }

    @Override
    public int doEndTag() throws JspException { // 表示輸出,如果沒有編寫,則沒有輸出

        if (super.bodyContent!=null) {
            try {
                super.bodyContent.writeOut(super.getPreviousOut());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return BodyTagSupport.EVAL_PAGE;        // 正常執行完畢
    }
}
           
public class BodyIteratorTagExtraInfo extends TagExtraInfo {

    @Override
    public VariableInfo[] getVariableInfo(TagData data) {

        return new VariableInfo[] {new VariableInfo(data.getId(), "java.lang.String", true, VariableInfo.NESTED)};
    }
}
           

配置(MyTag.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            
    <short-name>tag</short-name>                
    <tag>
        <name>bodyIterator</name>                       
        <tag-class>org.gpf.tag.MyTag</tag-class>
        <tei-class>org.gpf.tag.BodyIteratorTagExtraInfo</tei-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>name</name>                   
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
        <attribute>
            <name>scope</name>                  
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
        <attribute>
            <name>id</name>                 
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
    </tag>
</taglib>
           

web.xml

<jsp-config>
    <taglib>
        <taglib-uri>tag</taglib-uri>
        <taglib-location>/WEB-INF/MyTag.tld</taglib-location>
    </taglib>
</jsp-config>
           

在jsp頁面中使用以上标簽:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
  <%
    // 此代碼僅僅是測試,實際的操作應該交給Servlet完成
    List<String> all = new ArrayList<String>();
    all.add("Thinking in Java");
    all.add("鋒利的Jquery");
    all.add("陸雪琪");
    request.setAttribute("all", all);
   %>

    <mytag:bodyIterator name="all" scope="request" id="content">
        内容:${content },長度:<%=content.length() %><br />
    </mytag:bodyIterator>
  </body>
</html>
           

以上的content已經變成了一個變量,而這個變量的使用必須依靠

BodyIteratorTagExtraInfo

類的支援才得以完成。

使用BodyTagSupport類可以完成标簽的開發,但是開發的過程比直接使用TagSupport要麻煩很多。通過TagExtraInfo類和VariableInfo類可以直接将标簽中定義的屬性變成一個變量操作。

簡單标簽

在jsp1.2之前如果想要進行标簽庫的開發,要麼選擇繼承

TagSupport

類,要麼繼承

BodyTagSupport

類,而且還要去覆寫

doStartTag()

doAfterBody()

doEndTag()

方法,還必須非常清楚這些方法的傳回值,例如:

SKIP_BODY

EVAL_BODY_INCLUDE

等。到了jsp2.0簡化了标簽庫開發的複雜度,專門增加了一個制作簡單标簽的

SimpleTagSupport

類,直接覆寫裡面的

doTag()

方法即可。

SimpleTagSupport

類的定義如下:

方法 描述
public void doTag() 完成具體标簽功能的編寫
public JspContext getJspContext() 取得jsp上下文,主要是用于輸出
protected JspFragment getJspBody() 取得JspFragment對象,用于疊代輸出

例:格式化日期的簡單标簽

public class SimpleDateTag extends SimpleTagSupport {

    private String format; // 格式化模式

    public String getFormat() {
        return format;
    }

    public void setFormat(String format) {
        this.format = format;
    }


    @Override
    public void doTag() throws JspException, IOException {

        SimpleDateFormat sdf = new SimpleDateFormat(format);
        super.getJspContext().getOut().write(sdf.format(new Date()));
    }
}
           

标簽描述檔案:

<?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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            
    <short-name>tag</short-name>                
    <tag>
        <name>simpleDate</name>                     
        <tag-class>org.gpf.tag.SimpleDateTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>format</name>                 
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
    </tag>
</taglib>
           

在jsp使用标簽:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
    <mytag:simpleDate format="yyyy-MM-dd HH:mm:ss.SSS"/>
  </body>
</html>
           

使用簡單标簽進行疊代輸出:

public class SimpleIterator extends SimpleTagSupport {

    private String id;
    private String name;
    private String scope;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    @Override
    public void doTag() throws JspException, IOException {

        Object value = null;
        if("page".equals(scope))
            value = super.getJspContext().getAttribute(name,PageContext.PAGE_SCOPE);
        if("request".equals(scope))
            value = super.getJspContext().getAttribute(name,PageContext.REQUEST_SCOPE);
        if("session".equals(scope))
            value = super.getJspContext().getAttribute(name,PageContext.SESSION_SCOPE);
        if("application".equals(scope))
            value = super.getJspContext().getAttribute(name,PageContext.APPLICATION_SCOPE);

        if(value!=null&&value instanceof List<?>){
            Iterator<?>iterator = ((List<?>)value).iterator();
            while (iterator.hasNext()) {
                super.getJspContext().setAttribute(id, iterator.next());
                super.getJspBody().invoke(null);
            }
        }
    }
}
           

在标簽庫描述檔案中進行描述:

<?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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>            
    <short-name>tag</short-name>                
    <tag>
        <name>simpleIterator</name>                     
        <tag-class>org.gpf.tag.SimpleIterator</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>id</name>                 
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
            <attribute>
            <name>name</name>                   
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
            <attribute>
            <name>scope</name>                  
            <required>true</required>           
            <rtexprvalue>true</rtexprvalue>     
        </attribute>
    </tag>
</taglib>
           

在jsp頁面中使用标簽:

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
  <%
    List<String> all = new ArrayList<String>();
    all.add("張小凡");
    all.add("陸雪琪");
    all.add("碧瑤");
    all.add("周一仙");
    request.setAttribute("all", all);
   %>
    <mytag:simpleIterator name="all" scope="request" id="content">
        内容:${content }<br />
    </mytag:simpleIterator>
  </body>
</html>
           

DynamicAttributes接口

之前的标簽的所有屬性如果需要使用就必須在标簽描述檔案.tld檔案中使用

<attribute>

節點進行定義,如果屬性不是固定的而是由使用者自定義的就可以使用

DynamicAttributes

接口實作。

例:有使用者在前台傳入若幹數字,進行加法計算。

public class DynamicAddTag extends SimpleTagSupport implements DynamicAttributes {

    private Map<String, Float> num = new HashMap<String, Float>();

    @Override
    public void setDynamicAttribute(String uri, String localName, Object value)
            throws JspException {
        // 取出儲存的每一個屬性都儲存在Map中
        num.put(localName, Float.parseFloat(value.toString()));
    }

    @Override
    public void doTag() throws JspException, IOException {

        float sum = f;
        Iterator<Map.Entry<String, Float>> iterator = num.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Float> value = iterator.next();
            sum += value.getValue();
        }

        super.getJspContext().getOut().write(sum + ""); // 輸出
    }
}
           

标簽描述檔案:

<?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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd" version="2.1">
    <tlib-version>1.0</tlib-version>            
    <short-name>tag</short-name>                
    <tag>
        <name>add</name>                        
        <tag-class>org.gpf.tag.DynamicAddTag</tag-class>
        <body-content>empty</body-content>
        <dynamic-attributes>true</dynamic-attributes>
    </tag>
</taglib>
           

在jsp頁面中使用标簽庫

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%@taglib prefix="mytag" uri="tag"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hellotag.jsp</title>
  </head>
  <body>
    計算結果:<br />
    <mytag:add num1="1.0" num2="2.0" num3="3.0" num4="-2.1"/>
  </body>
</html>
           

運作結果:

JavaWeb中級