《JavaWeb從入門到改行》JSP+EL+JSTL大雜燴湯


title: Servlet之JSP
tags: []
notebook: javaWEB
---javascript

JSP是什麼 ?

JSP就是Servlet,全名是"JavaServer Pages" 。由於Servlet不適合設置html響應體,須要大量的response.getWriter().print("<html>"),而和html是靜態頁面,不能包含動態信息。JSP完美的解決了二者的缺點,在原有html的基礎上添加java腳本,構成jsp頁面。css

JSP的運行機理

當jsp頁面第一次被訪問時,服務器會經過實現HttpJspPage接口(javax.servlet.jsp包下的接口)把jsp轉換成Servlet,也就是java文件(在tomcat的work目錄下能夠找到jsp轉換成.java源代碼),下圖是jsp轉成Servlet的樣子。html

上圖中,JSP頁面被翻譯成了Servelt ,能夠看出JSP頁面的主體被轉換成了一個_jspService()方法,即實現了HttpJspPage接口。而後再把java編譯成.class,再建立該類對象,最後調用它的service()方法完成對客戶端的響應(輸出內容到客戶端) 。 當第二次調用同一jsp時,直接調用service()方法。因此第一次的時間老是很長,常稱爲"第一次懲罰" 。java

JSP與Servlet的分工

jspweb

  • 做爲請求發起頁面,例如顯示錶單、超連接。
  • 做爲請求結束頁面,例如顯示數據 。

Servletsql

  • 做爲請求中處理數據的環節。

三大指令+三大java腳本+動做標籤

JSP的組成 = html + java腳本 + jsp標籤(指令)數據庫

3種java腳本

  • <%...%>: Scriptlet,就是java代碼片斷(經常使用) 。能放在裏面的內容,至關於java中方法的內容
  • <%=...%>:java表達式,用於輸出(經常使用),用於輸出一條表達式(或變量)的結果。至關於response.getWriter().print( ... );裏面的內容
  • <%!...%>:聲明(幾乎不用),用來建立類的成員變量和成員方法 。 至關於類體中放的內容JSP標籤

三個指令

指令格式: <%@指令名 屬性=值 屬性=值 ..........%>apache

page指令(重要)數組

重要屬性:瀏覽器

  • pageEncoding:它指定當前jsp頁面的編碼 。
  • contentType:它表示添加一個響應頭:Content-Type!等同於response.setContentType("text/html;charset=utf-8");
  • import:導包!<%@page import="java.net.*,java.util.*,java.sql.*"%>
  • errorPage:當前頁面若是拋出異常,那麼要轉發到哪個頁面,由errorPage來指定 。能夠在web.xml中配置錯誤頁面
  • isErrorPage:它指定當前頁面是否爲處理錯誤的頁面!當該屬性爲true時,這個頁面會設置狀態碼爲500!並且這個頁面可使用9大內置對象中的exception!
  • isELIgnored:是否忽略el表達式,默認值爲false,不忽略,即支持!
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

include指令---->靜態包含

與RequestDispatcher的include()方法的功能類似!<%@include%> 它是在jsp編譯成java文件時完成的!若是A 頁面包含B頁面,那麼他們共同生成一個java(就是一個servlet)文件,而後再生成一個class!RequestDispatcher的include()是一個方法,包含和被包含的是兩個servlet,即兩個.class!他們只是把響應的內容在運行時合併了!

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

須要注意的是,一個被包含的頁面不能出現與包含頁面相同的代碼,好比一些``標籤等 。這個指令的做用使一些不變的東西可重用

taglib指令---->導入標籤庫

兩個屬性;

  • prefix:指定標籤庫在本頁面中的前綴!由咱們本身來起名稱!
  • uri: 指定標籤庫的位置!

用法在導入JSTL標籤中會用到

JSP動做標籤

動做標籤是由tomcat(服務器)來解釋執行!它與java代碼同樣,都是在服務器端執行的!

  • <jsp:forword>:轉發!它與RequestDispatcher的forward方法是同樣的,一個是在Servlet中使用,一個是在jsp中使用!
  • <jsp:include>:包含:它與RequestDispatcher的include方法是同樣的,一個是在Servlet中使用,一個是在jsp中使用!

<jsp:include><%@include>的區別是: <jsp:include>在編譯之間就把包含和被包含頁面合併成一個java文件,編譯成一個.class。而<%@include>是包含和被包含頁面各自編譯,而後包含頁面去調用被包含頁面的.class 。

  • <jsp:param>:它用來做爲forward和include的子標籤!用來給轉發或包含的頁面傳遞參數!
/**
* 包含頁
*/
<h1>a.jsp</h1>
<%--動態包含 --%>
<jsp:include page="b.jsp" >
            <jsp:param value="zhangSan" name="username"/>
            <jsp:param value="123" name="password"/>
</jsp:include>            
/**
* 被包含頁
*/
<h1>b.jsp</h1>
<%
    String username = request.getParameter("username");
    String password = request.getParameter("password");
%>
/**
* <h1>a.jsp</h1>不會顯示,只顯示<h1>b.jsp</h1>。 由於包含動做標籤是它與RequestDispatcher的include方法是同樣的。既然包含了其餘頁面,當前頁面就算了
*/

JSP九大內置對象和PageContext域對象

內置對象 介紹 是哪一個類的實例(對象)
out jsp的輸出流,用來向客戶端響應 javax.servlet.jsp.JspWriter
page 當前jsp對象!當前JSP頁面的"this " javax.servlet.jsp.HttpJspPage
pageContext 頁面上下文對象 ,四大域對象之一 javax.servlet.jsp.PageContext
exception 只有在錯誤頁面中可使用這個對象 java.lang.Throwable
config 就是Servlet中的ServletConfig 類的對象 javax.servlet.ServletConfig
request 就是HttpServletRequest類的對象 javax.servlet.http.HttpServletRequest
response 就是HttpServletResponse類的對象 javax.servlet.http.HttpServletResponse
application 就是ServletContext類的對象 javax.servlet.ServletContext
session 就是HttpSession類的對象 javax.servlet.http.HttpSession

什麼是內置對象

在JSP中一般會用到上述的九個對象,爲了不在JSP中出現繁瑣定義對象的語句,索性直接先定義好上述的九個對象,而且各自給對象起了名字,當咱們用的時候,無需再定義對象,如HttpServletRequest request = new HttpServletRequest(),直接用request對象就能夠了。並且JSP的本質就是Servlet ,咱們寫的JSP頁面都會被翻譯成Servlet去執行,能夠這麼說,JSP和Servlet中的對象是通用的。因此在Servlet中域對象中存儲的值,在JSP中直接就能夠得到。這是很是方便的。
ServletConfig、HttpServletRequest、HttpServletResponse 、ServletContext、HttpSession 的學習 請點擊這兒學習 。

PageContext域對象與pageContext內置對象

PageContext是javaweb四大域對象之一,又稱爲page域,並且只有在JSP中有,Servlet沒有 . PageContext做爲內置對象,同時也是域對象之一,可以存取數據。並且PageContext一個頂九個,很是重要 。

  • 在一個jsp頁面中共享數據!這個域是在當前jsp頁面和當前jsp頁面中使用的標籤之間共享數據!
abstract  java.lang.Object getAttribute(java.lang.String name) 
          Returns the object associated with the name in the page scope or null if not found. 
abstract  void setAttribute(java.lang.String name, java.lang.Object value) 
          Register the name and value specified with page scope semantics.
  • (page域特有)這個域對象能夠代理其餘域,可以向其餘域中存取東西pageContext.setAttribute("xxx", "XXX", PageContext.SESSION_SCOPE)
abstract  java.lang.Object getAttribute(java.lang.String name, int scope) 
         Return the object associated with the name in the specified scope or null if not found. 
abstract  void setAttribute(java.lang.String name, java.lang.Object value, int scope) 
          Register the name and value specified with appropriate scope semantics.
  • (page域特有)全域查找(重要),在四大域中都能查找。 從小到大查找,小域優先大域。
abstract  java.lang.Object findAttribute(java.lang.String name) 
          Searches for the named attribute in page, request, session (if valid), and application scope(s) in order and returns the value associated or null.
  • (pageContext內置對象特有)一個頂九個,可以獲取其餘的8個內置對象,也就是說,PageContext一個內置對象就能夠當九個內置對象用。
abstract  JspWriter getOut() 
          The current value of the out object (a JspWriter). 
abstract  java.lang.Exception getException() 
          The current value of the exception object (an Exception). 
abstract  java.lang.Object getPage() 
          The current value of the page object (In a Servlet environment, this is an instance of javax.servlet.Servlet). 
abstract  ServletRequest getRequest() 
          The current value of the request object (a ServletRequest). 
abstract  ServletResponse getResponse() 
          The current value of the response object (a ServletResponse). 
abstract  ServletConfig getServletConfig() 
          The ServletConfig instance. 
abstract  ServletContext getServletContext() 
          The ServletContext instance. 
abstract  HttpSession getSession() 
          The current value of the session object (an HttpSession).

javaweb四大域對象與jsp九大內置對象

點擊這兒

javaBean解析

什麼是javabean ?

JavaBean是一種規範,也就是對類的要求。要求以下:

  • 必需要爲成員提供get/set方法(也就是讀方法和寫方法)(二者只提供一個也是能夠的)。
    • 對於有get/set方法的成員變量稱之爲屬性
    • 屬性名是由set/get方法決定的,不是由成員名字決定的。 好比 String name ; public void setUserName(){....}。屬性名是userName,而不是name .
    • 有get/set方法的成員,可是沒有成員也是能夠的。這也是有屬性
  • 必需要有默認構造器(沒參的)
  • boolean類型的屬性,它的讀方法能夠是is開頭,也能夠是get開頭!
public class Person {
    private String name;  //成員
    private int age; 
    private boolean bool;     //boolean類型成員
    public boolean isBool() {   //讀方法
        return bool;
    }
    public void setBool(boolean bool) {
        this.bool = bool;
    }
    public String getId() {  // 就算沒有成員id,也是有id屬性的
        return "fdsafdafdas";
    }
    public String getUserName() {
        return name;
    }
    public void setName(String username) {  //就算成員名字是name,可是屬性名字仍是userName 。
        this.name = name;
    }
    public int getAge() {  //讀方法
        return age;
    }
    public void setAge(int age) { //寫方法
        this.age = age;
    }
    public Person() {  //必須有默認的無參的構造函數
        super();
        // TODO Auto-generated constructor stub
    }
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", gender=" + gender
                + "]";
    }
    public Person(String name, int age, String gender) {
        super();
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}

操做javaBean的方法(一): 內省(通常不用)

內省,自我檢討。 底層依賴的是反射 ,經過反射來操做javabean 。

如定義上述JavaBean(Person類),其成員是私有的。固然能夠經過反射去訪問Person類的私有成員,可是有危險。 通常都是經過get/set方法來操做私有成員 。 內省的目標就是獲得JavaBean屬性的讀、寫方法(get/set)。 這樣就能經過set/get方法操做javabean. 經過內省操做javabean的方式是這樣的:

  1. 經過內省類Introspector的getBeanInfo方法返回BeanInfo 對象
  2. 經過接口BeanInfo的getMethodDescriptors() 方法獲得全部屬性描述符對象PropertyDescriptor
  3. 經過類PropertyDescriptor的getReadMethod() 和getWriteMethod()方法,也就是get/set方法

具體API的方法以下:(javaSE6.0 API)

static BeanInfo getBeanInfo(Class<?> beanClass) 
          在 Java Bean 上進行內省,瞭解其全部屬性、公開的方法和事件。 
 PropertyDescriptor[] getPropertyDescriptors() 
          得到 beans PropertyDescriptor。  
 Method getReadMethod() 
          得到應該用於讀取屬性值的方法。 
 Method getWriteMethod() 
          得到應該用於寫入屬性值的方法。

操做javaBean的方法(二): 使用Commons-beanutils 工具(方便簡單,經常使用)

使用內省的方法當然能行,可是太過繁瑣 。 commons-beanutils這個工具, 它底層使用了內省,對內省進行了大量的簡化! 因此要導入這個工具

  • commons-beanutils-1.8.3.jar 下載
  • commons-logging-1.1.1.jar 下載

下面的代碼完美演示了Commons-beanutils 工具對javabean的操做。 (javabean 用上述的Person類)

/**
* 使用BeanUtils工具來操做User類(javabean)
*/
import org.apache.commons.beanutils.BeanUtils;
public void fun1() throws Exception {
    /**
    *    反射
    */
    String className = "包.Person";
    Class clazz = Class.forName(className);
    Object bean = clazz.newInstance();
    /**
    *   利用setProperty設置 屬性
    */  
    BeanUtils.setProperty(bean, "name", "張三");
    BeanUtils.setProperty(bean, "age", "23");   //會自動將字符串轉換成整形 。
    BeanUtils.setProperty(bean, "gender", "男");   //就算是Person中沒有gender這個屬性,同樣不會報錯
    /**
    *   利用getProperty獲取 屬性值
    */      
    String age = BeanUtils.getProperty(bean, "age");
    System.out.println(age); //輸入單個屬性
    System.out.println(bean);  //調用Person中的toString()方法總體輸出
    }
/**
*把map中的屬性直接封裝到一個bean中
*把map的數據封裝到一個javabean中!要求map的key與bean的屬性名相同!
*/
public void fun2() throws Exception {
    /**
    * 建立Map  
    */
    Map<String,String> map = new HashMap<String,String>();
    map.put("username", "zhangSan");
    map.put("age", "123");
    /**
    * 新建bean對象
    */      
    Person person = new Person();
    /**
    *  map中的屬性直接封裝到bean中
    */
    BeanUtils.populate(person, map);        
    System.out.println(person);
}
/**
* 把map中的數據封裝到Person中的第二種形式。 更加簡化了代碼
*/
/**
* 編寫CommonUtils類 
*/
public class CommonUtils {
    /**
     * 生成不重複的32位長的大寫字符串
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }
    
    /**
     * 把map轉換成指定類型的javaBean對象
     */
    public static <T> T toBean(Map map, Class<T> clazz) {
        try {
            /*
             * 1. 建立指定類型的javabean對象
             */
            T bean = clazz.newInstance();
            /*
             * 2. 把數據封裝到javabean中
             */
            BeanUtils.populate(bean, map);
            /*
             * 返回javabean對象
             */
            return bean;
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
}
/**
* 把map中的數據封裝到Person中
*/
public void fun3() {
        Map<String,String> map = new HashMap<String,String>();
        map.put("username", "zhangSan");
        map.put("password", "123");
        /**
        * 一句代碼完成封裝
        */
        User user = CommonUtils.toBean(map, User.class);
        System.out.println(user);
    }

jsp中經過標籤操做javaBean (過期的東西)

<jsp:useBean> <jsp:setProperty><jsp:getProperty>,這三個標籤在現在的model II 年代已通過時了 。 想學的自行百度

EL表達式初窺門徑

EL是什麼? EL的做用是什麼

JSP2.0要求把html和css分離、要把html和javascript分離、要把Java腳本替換成標籤 。 而El表達式就是要替換掉java腳本中的輸出腳本<%=...%>,也就是說EL表達式只能作輸出用 。 使用EL標籤的好處就是非java人員也可使用,EL全程是「Expression Language 」

EL的語法

若是要輸出的結果是一個對象中的屬性,能夠利用[].來訪問該屬性,就至關於調用get方法: 格式爲: ${對象.屬性}或者${對象["屬性"]}。當屬性不是有效的java變量名稱時,只能用${對象["屬性"]}這種形式。

  • 操做list和數組: ${list[0]}
  • 操做bean屬性 : ${person.name}、${person[‘name’]} ,稱爲javaBean導航
  • 操做Map的值:${map.key}、${map[‘key’]}

EL的11大內置對象

域相關的內置對象

內置對象 相關功能
pageScope 可以輸出各自域總的數據
requestScope
sessionScope
applicationScope


若是是${xxx}這種形式,就是全域查找名爲xxx的屬性,若是不存在,輸出空字符串,而不是null。四個域都存在,按照小的來。千萬不要當成是輸出xxx字符串。

/**
* Class Address
*/
public class Address {
    private String city;
    private String street;
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    @Override
    public String toString() {
        return "Address [city=" + city + ", street=" + street + "]";
    }   
}
/**
* Class Person
*/
public class Employee {
    private String name;
    private double salary;
    private Address address;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Employee [name=" + name + ", salary=" + salary + ", address="
                + address + "]";
    }
}
/**
* jsp中輸出Person的地址信息
*/
<%
    Address address = new Address();
    address.setCity("昆明");
    address.setStreet("昆明理工大學");
    
    Person p = new Person();
    p.setName("李小四");
    p.setSalary(123456);
    p.setAddress(address);
    <!--request域存數據-->
    request.setAttribute("person", p);
%>
    <!--使用EL表達式輸出-->
    ${requestScope.emp.address.street }

其餘的內置對象

  • param: Map<String, String[]>類型,用來獲取請求參數。參數的名字就是鍵的名字。相似於${request.getparameter()},不一樣的是,即便參數不存在,返回空字符串,而不是null。 用法爲${param.name}
  • paramValues:Map<String, String[]>類型,也是用來獲取參數,當一個參數名對應多個參數值的時候使用。多個參數值以字符串數組的形式存在。因此用法大多數是paramValues.hobby[0]
  • header : Map<String,String>類型,用來獲取請求頭 ,如獲取主機名:${header.Host}
  • headerValues: Map<String,String>類型,用來獲取多值請求頭 ,如獲取accept-language標頭的第一個值${headerValue["accept-language"][0]}
  • initParam :Map<String,String>類型。它對應web.xml文件中的 參數 。 如獲取XXXX, ${initParam.aaa}
<context-param>
    <param-name>aaa</param-name>
    <param-value>XXX</param-value>
  </context-param>
  • cookie : Map<String,Cookie>類型,其中key是Cookie的名字,而值是Cookie對象自己。 例如要獲取cookie名字爲uname的名字和值${cookie.unam.name}${cookie.unam.value}
  • pageContext: PageContext類型 。在前文中學過,pageContext這個內置對象是一個頂九個,能夠得到其餘8個內置對象,因此輸出的時候也能夠用來輸出其餘內置對象的東西。

${pageContext.request.contextPath},先得到request對象,而後在調用request的contextpath方法獲取到的結果是 :/項目名。 如獲取session的ID${pageContext.session.id }

注意的是:項目名字可能會更改,那麼代碼中含有項目名的路徑就須要更改,因此,代碼有項目名的路徑,通常都要用${pageContext.request.contextPath}這種方法來獲取項目名,如超連接中:<a href="${pageContext.request.contextPath }/文件名/XX.jsp">點擊這裏</a>。 表單中:<form action="${pageContext.request.contextPath }/文件名/XXX" method="post">

EL運算符

EL做爲輸出的表達式,固然用能夠進行計算,如${1+3} 。常見的運算符幾乎和常見的運算同樣,不在累贅 。

官方的EL函數庫與自定義EL函數

什麼是EL函數庫? 怎麼使用 ?

EL函數庫是由第三方對EL的擴展,JSTL的函數庫最是出名。EL函數庫裏定義了一些有返回值的靜態方法,而後經過EL來調用它們,這些函數庫裏面的函數是定義好的,能夠說就是官方的函數,固然咱們能夠本身定義函數庫。官方的函數包含了不少對字符串的操做方法,以及對集合對象的操做。

JSP頁面導入函數庫

由於是第三方的函數庫,因此在JSP頁面中要使用函數庫中的函數,須要使用taglib指令導入函數庫
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>,其中 prefix和uri的值其實就是 若是用的MyEclipse開發工具的話,咱們沒有必需要導入這個包,由於在發佈到服務器上時候會在tomcat目錄下的web-inf下lib自動存在關於jsf的相關包,因此不須要人爲的導入什麼jar包。咱們作的只是在jsp頁面添加這個Page指令而已 。在這幾個關於jsf的包中,打開jstl-版本號.jar-->META-INF下的fn.tld。 在這裏面可以發現fnhttp://java.sun.com/jsp/jstl/functions。 這就是這兩個參數的由來。

函數庫中函數 與 EL調用這些函數

函數庫中的函數以下:

  • String toUpperCase(String input):把參數轉換成大寫
  • String toLowerCase(String input):把參數轉換成小寫
  • int indexOf(String input, String substring):從大串,輸出小串的位置!
  • boolean contains(String input, String substring):查看大串中是否包含小串
  • boolean containsIgnoreCase(String input, String substring):忽略大小寫的,是否包含
  • boolean startsWith(String input, String substring):是否以小串爲前綴
  • boolean endsWith(String input, String substring):是否以小串爲後綴
  • String substring(String input, int beginIndex, int endIndex):截取子串
  • String substringAfter(String input, String substring):獲取大串中,小串所在位置後面的字符串
  • substringBefore(String input, String substring):獲取大串中,小串所在位置前面的字符串
  • String escapeXml(String input):把input中「<」、">"、"&"、"'"、""",進行轉義
  • String trim(String input):去除先後空格
  • String replace(String input, String substringBefore, String substringAfter):替換
  • String[] split(String input, String delimiters):分割字符串,獲得字符串數組
  • int length(Object obj):能夠獲取字符串、數組、各類集合的長度!
  • String join(String array[], String separator):聯合字符串數組!

用EL表達式調用這些函數的案例:

<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
…
String[] strs = {"a", "b","c"};
List list = new ArrayList();
list.add("a");
pageContext.setAttribute("arr", strs);
pageContext.setAttribute("list", list);
%>
${fn:length(arr) }<br/><!--3-->
${fn:length(list) }<br/><!--1-->
${fn:toLowerCase("Hello") }<br/> <!-- hello -->
${fn:toUpperCase("Hello") }<br/> <!-- HELLO -->
${fn:contains("abc", "a")}<br/><!-- true -->
${fn:containsIgnoreCase("abc", "Ab")}<br/><!-- true -->
${fn:contains(arr, "a")}<br/><!-- true -->
${fn:containsIgnoreCase(list, "A")}<br/><!-- true -->
${fn:endsWith("Hello.java", ".java")}<br/><!-- true -->
${fn:startsWith("Hello.java", "Hell")}<br/><!-- true -->
${fn:indexOf("Hello-World", "-")}<br/><!-- 5 -->
${fn:join(arr, ";")}<br/><!-- a;b;c -->
${fn:replace("Hello-World", "-", "+")}<br/><!-- Hello+World -->
${fn:join(fn:split("a;b;c;", ";"), "-")}<br/><!-- a-b-c -->

${fn:substring("0123456789", 6, 9)}<br/><!-- 678 -->
${fn:substring("0123456789", 5, -1)}<br/><!-- 56789 -->
${fn:substringAfter("Hello-World", "-")}<br/><!-- World -->
${fn:substringBefore("Hello-World", "-")}<br/><!-- Hello -->
${fn:trim("     a b c     ")}<br/><!-- a b c -->
${fn:escapeXml("<html></html>")}<br/> <!-- <html></html> -->

自定義EL函數庫

自定義EL函數庫的步驟

  1. 寫一個類,寫一個有返回值的靜態方法
  2. 編寫xxx.tld文件(案例以kmust.tld文件爲例)。把xxx.tld文件放在/WEB-INF目錄下
  3. 在頁面中添加taglib指令,導入自定義標籤庫

第一步:寫一個有返回值的靜態方法的類

/**
* Class MyFunctions.java
* 這個類中寫有返回值的靜態方法,也就是咱們自定義的函數
*/
package cn.kmust.fn;
public class KmustFunctions {
    public static String func1() {
         return "這是我本身定義的函數" ;
    }
}

第二步: 編寫kmust.tld文件。把kmust.tld文件放在/WEB-INF目錄下

<?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_0.xsd"
  version="2.0">
    
  <tlib-version>1.0</tlib-version>
  <short-name>it</short-name>
  <uri>http://www.kmust.cn/el/functions</uri>
  
  <function>
    <name>func1</name>
    <function-class>cn.kmust.fn.MyFunction</function-class>
    <function-signature>java.lang.String func1()</function-signature>
  </function>
  
</taglib>

第三步 : 在頁面中添加taglib指令,導入自定義標籤庫

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="it" uri="/WEB-INF/tlds/kmust.tld" %>
<html>
  <body>
<h1>${it:func1() } </h1>
  </body>
</html>

JSTL標籤庫

什麼是JSTL標籤庫

JSTL是Apache對EL表達式的擴展,依賴EL 。JSTL是標籤語言。JSP在2.0之後開始放棄java腳本,EL表達式代替<%=...%>解決了輸出的問題。而JSTL標籤將要代替<% ...%> 解決另外的Java腳本問題。

導入標籤庫

同EL函數庫的導入類似,若是使用MyEclipse開發工具,則不須要人爲導入jar包,由於項目發佈到Tomcat時,MyEclipse會在lib目錄下存放jstl的jar包。咱們只須要在JSP頁面中使用taglib指令導入標籤庫便可。
jstl一共有四個標籤庫,分別是:

  • core: 核心標籤庫 (重點)。 由於前綴是c,因此又叫作c標籤庫
  • fmt: 格式化標籤庫 (裏面有兩個重要標籤)。由於前綴是fmt,因此又叫作c標籤庫
  • SQL: 數據庫標籤庫 (過期了)
  • XML: xml標籤庫 (過期了)

以導入core核心標籤庫爲列: <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

core標籤庫經常使用標籤

  1. out標籤<c:out>:輸出

    • value屬性:能夠是字符串常量,也能夠是EL表達式
    • default屬性:當輸出的內容爲null時,會輸出defaulst指定的值
    • escapeXml屬性: 默認爲true,表示轉義
  2. set標籤<c:set>:設置(能夠建立域的屬性)
    • var屬性:變量名
    • value屬性: 變量值,能夠是EL表達式
    • scope屬性: 域,若是不寫,默認爲page域。 可選的值: page、request、session、application
    <c:set var="code" value="<script>alert('hello');</script>" scope="request"/> // 在request域中添加name爲a,值爲彈窗hello的數據
    <c:out value="${code }" /> //全域查找,輸出nme爲a的值。 而且把<script>標籤給轉義了。全部直接輸出字符串,而不是彈窗
  3. remove標籤<c:remove>:刪除域變量
    • var屬性: 變量名
    • scope屬性 : 若是不給出scope這個屬性,表示刪除全部域中的該名稱的變量;若是指定了域,只刪除該域的變量
  4. URL標籤<c:url>:指定路徑
    • var屬性: 指定變量名,一旦添加了這個屬性,那麼URL標籤就不會再輸出到頁面,而是把生成的URL保存到域中
    • value屬性: 指定的路徑。 會在路徑前面自動添加項目名<c:url value="/index.jsp"> = ${pageContext.request.contextpath}/index.jsp=/項目名字/index.jsp。 因此超連接或者表單中的路徑,可使用前兩種均可以。
    • scope屬性 : 與var屬性一塊兒使用把URL保存到域中。
    <a href="<c:url value='/index.jsp'/>">點擊這裏回到主頁</a>
    <c:url value='/index.jsp'>
      <c:param name="name" value="張三"/> //加了參數,若是參數包含中文,則自動URL編碼 ,輸出:/項目名/index.jsp?username=A%E%G%D%G%D%G%D%
    </c:url>
  5. if標籤<c:if>:對應java中的if語句
    • ... ,當test爲值時,執行標籤體內容!
    <c:if test="${empty param.name }"> //參數的name不爲空
     輸出hello  
    </c:if>
  6. choose標籤<c:choose>:對應Java中的if/else
<c:choose>
      <c:when test="">...</c:when>
      <c:when test="">...</c:when>
      <c:when test="">...</c:when>
       ... 
      <c:otherwise> ...</c:otherwise>
    </c:choose>
    等同與
    if(...) {
    } else if( ....) {
    } else if( ....) {
    } else if( ....) {
    } ...
    else { ...}
  1. forEach標籤<c:forEach>:用來循環遍歷數組、集合。能夠用計數方式來循環
    • var:循環變量
    • begin:設置循環變量從幾開始。
    • end:設置循環變量到幾結束。
    • step:設置步長!等同與java中的i++,或i+=2。step默認爲1
    <c:forEach var="i" begin="1" end="10" step="2">//for循環
     ${i }<br/>
    </c:forEach>
    用來輸出數組、集合
    • items:指定要循環誰,它能夠是一個數組或一個集合
    • var:把數組或集合中的每一個元素賦值給var指定的變量。
    <%
     String[] strs = {"one", "two"};
     request.setAttribute("strs", strs);
    %>
    <c:forEach items="${strs }" var="str">//輸出數組和集合
     ${str }<br/>
    </c:forEach>
    循環狀態
    • count:循環元素的個數
    • index:循環元素的下標
    • first:是否爲第一個元素
    • last:是否爲最後一個元素
    • current:當前元素
      ```java
      <%
      ArrayList list = new ArrayList ();
      list.add("一");
      list.add("二");
      list.add("三");
    pageContext.setAttribute("list", list);
    %>
    //循環狀態
    ${vs.index} ${vs.count } ${vs.first } ${vs.last } ${vs.current }

```

fmt標籤庫經常使用標籤

fmt標籤庫是用來格式化輸出的,一般須要格式化的有時間和數字

  1. 對時間的格式化 :<fmt : formatDate value="" pattern="" />
    • value: 指定一個Date類型的變量
    • pattern: 用來指定輸出的模板 ,例如yyyy-MM-dd HH:mm:ss
<%
    Date date = new Date();
    request.setAttribute("date", date);
    
%>
<fmt:formatDate value="${requestScope.date }" pattern="yyyy-MM-dd HH:mm:ss"/>   // 按照給定的格式輸出時間
  1. 對數字的格式化:
<%
    request.setAttribute("num1", 3.1415926);
%>
<fmt:formatNumber value="${requestScope.num1 }" pattern="0.000"/><br/> //按照0.000保留小數點後面的位數,四捨五入,不足補0
<fmt:formatNumber value="${requestScope.num1 }" pattern="#.###"/>  //按照#.###保留小數點後面的位數,四捨五入,不足不補0

自定義標籤

自定義標籤步驟:

  1. 標籤處理類
    須要實現SimpleTag接口(javaSE下),其接口下的方法:
void doTag()   //每次執行標籤時都會調用這個房
          Called by the container to invoke this tag. 
 JspTag getParent()  //返回父標籤
          Returns the parent of this tag, for collaboration purposes. 
 void setJspBody(JspFragment jspBody)  //設置 標籤體 
          Provides the body of this tag as a JspFragment object, able to be invoked zero or more times by the tag handler. 
 void setJspContext(JspContext pc)  //設置jsp上下文對象,兒子就是PageContext,通常都是用pageContext
          Called by the container to provide this tag handler with the JspContext for this invocation. 
 void setParent(JspTag parent) 
          Sets the parent of this tag, for collaboration purposes.
public class MyTag1 implements SimpleTag {
    private PageContext pageContext;
    private JspFragment body;
    /**
     * 全部的setXxx()方法都會在doTag()方法以前被tomcat調用!
     * 所在doTag()中就可使用tomcat傳遞過來的對象了。
     */
    public void doTag() throws JspException, IOException {
        pageContext.getOut().print("Hello Tag!");
    }
    public JspTag getParent() {
        return null;
    }
    public void setJspBody(JspFragment body) {
        this.body = body;
    }
    public void setJspContext(JspContext context) { 
        this.pageContext = (PageContext) context;
    }
    public void setParent(JspTag arg0) {}
}

標籤處理類的這些方法都是由Tomcat調用:過程以下;

  • 當容器(Tomcat)第一次執行到某個標籤時,會建立標籤處理類的實例
  • 而後調用setJspContext(JspXontext)方法,把當前JSP頁面的pageContext對象傳遞給這個方法
  • 若是當前標籤有父標籤。那麼使用父標籤的標籤處理類對象調用setParent(JspTag)方法
  • 若是標籤有標籤體,那麼把標籤體轉換成JSPFragment對象,而後調用setJSPBody()方法
  • 每次執行標籤時,都調用doTage()方法,它是標籤處理方法
    實現SimpleTag接口過於繁瑣,有專門的一個類SimpleTagSupport能夠繼承,只須要重寫doTag()方法就能夠了,由於這個類幫咱們建立寫好其餘的方法。能夠經過getXX()的方法獲取其餘方法。具體的方法以下;
void doTag() 
          Default processing of the tag does nothing. 
static JspTag findAncestorWithClass(JspTag from, Class<?> klass) 
          Find the instance of a given class type that is closest to a given instance. 
protected  JspFragment getJspBody() 
          Returns the body passed in by the container via setJspBody. 
protected  JspContext getJspContext() 
          Returns the page context passed in by the container via setJspContext. 
 JspTag getParent() 
          Returns the parent of this tag, for collaboration purposes. 
 void setJspBody(JspFragment jspBody) 
          Stores the provided JspFragment. 
 void setJspContext(JspContext pc) 
          Stores the provided JSP context in the private jspContext field. 
 void setParent(JspTag parent) 
          Sets the parent of this tag, for collaboration purposes.
  1. 編寫tld文件,放在WEN-INF下
<tag>
    <name></name> 指定當前標籤的名稱
    <tag-class></tag-class> 指定當前標籤的標籤處理類!
    <body-content></body-content> 指定標籤體的類型
  </tag>

標籤體的內容有以下四種;
|內容|說明|
| :---| :---|
|empty|無標籤體(經常使用)|
|scriptless|能夠是EL表達式或者其餘的標籤或者字符串(經常使用)|
|JSP|(不使用)|
|tagdependent|(不使用)|

  1. 頁面中使用<%@taglib %>來導入tld文件
    <%@ taglib prefix= uri= %>

自定義標籤進階 1 : 再也不執行標籤下面的內容

能夠設置,若是執行這個標籤,後面的標籤都會不執行。實現這一功能的方法是在標籤處理類中的doTag()方法中使用SkippageException來結束! Tomcat會調用標籤處理類的doTag()方法,而後Tomcat會獲得SkipPageException,它會跳過頁面其餘內容

public void doTag() throws JspException, IOException {
        
        throw new SkipPageException();//拋出這個異常後,在本標籤後面的內容,將看不到!
    }

自定義標籤進階 2 :標籤屬性

添加屬性的步驟

  • 爲標籤處理類添加屬性,屬性須要一個set()方法,這個set()方法會在doTag()方法以前被Tomcat執行,因此doTag()中就可使用屬性了。
  • 在tld文件中對屬性進行配置
<attribute>
        <name>test</name> 指定屬性名
        <required>true</required> 指定屬性是否爲必需的
        <rtexprvalue>true</rtexprvalue> 指定屬性是否可使用EL
</attribute>

自定義標籤小案例 :

/**
* Class MyTag1
* 繼承SimpleTagSupport類,重寫doTag()方法。沒有標籤體
*/
package cn.kmust.tag;
public class MyTag1 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
       //由於這個SimpleTagSupport中早就爲咱們寫好了出doTag()以外的其餘方法,因此經過this.getXXX()便可得到其餘方法的返回對象。
        this.getJspContext().getOut().print("Hello one !");  //經過getJspContext得到pageContext,而後getOut得到輸出對象,經過print像頁面輸出 。
    }
}
/**
* Class MyTag2
* 有標籤體
*/
package cn.kmust.tag;
public class MyTag3 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        Writer out = this.getJspContext().getOut();//獲取當前jsp頁面的輸出流
        this.getJspBody().invoke(out);//執行標籤體內容,把結果寫到指定的流中,即頁面上。
        //須要說明的是,invoke()的參數能夠寫成null, 若是是null的話,表示使用的就是當前頁面的out !this.getJspBody().invoke(null);
    }
}
/**
* Class MyTag3
* 拋出SkipPageException異常,若是執行這個標籤,則後面的標籤都不會再執行了
*/
package cn.kmust.tag;
public class MyTag3 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        this.getJspContext().getOut().print("只能看到我,下面什麼都沒有!");
        throw new SkipPageException();//拋出這個異常後,在本標籤後面的內容,將看不到!
    }
}
/**
* Class  MyTag4
* 帶有一個屬性
*/
package cn.kmust.tag;
public class MyTag4 extends SimpleTagSupport {
    private boolean test; //定義這個屬性
    /*
     * 這個方法會由tomcat來調用,而且在doTag()以前
     */
    public void setTest(boolean test) {
        this.test = test;
    }
    public void doTag() throws JspException, IOException {
        if(test) {
            /*
             * 執行標籤體
             */
            this.getJspBody().invoke(null);//若是傳遞的輸出流爲null,表示使用的就是當前頁面的out!
        }
    }
}
/**
* tld文件的配置 。 名字是kmust-tag.tld  。 位置: WEN-INF/tlds
*/
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
   
  <tlib-version>1.0</tlib-version>
  <short-name>kmust</short-name>
  <uri>http://www.kmust.cn/tags/it-1.0</uri> //這是uri是隨便起的

  <tag>
    <name>myTag1</name>
    <tag-class>cn.kmust.tag.MyTag2</tag-class>  //標籤處理類的位置
    <body-content>empty</body-content>  //沒有標籤體
  </tag> 
  
  <tag>
    <name>myTag2</name>
    <tag-class>cn.kmust.tag.MyTag3</tag-class>
    <body-content>scriptless</body-content>  //有標籤體
  </tag>
  
  <tag>
    <name>myTag3</name>
    <tag-class>cn.kmust.tag.MyTag4</tag-class>
    <body-content>empty</body-content>
  </tag>
  
  <tag>
    <name>myTag4</name>
    <tag-class>cn.kmust.tag.MyTag5</tag-class>
    <body-content>scriptless</body-content>
    <attribute> //屬性
        <name>test</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
</taglib>
/**
* jsp頁面中利用自定義的標籤
*/
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="it" uri="/WEB-INF/tlds/kmust-tag.tld" %>   //導入自定義的tld文件的路徑
<html>
<body>
    <it:myTag4 test="${empty param.xxx }">  //test屬性的值是 「參數」 ,標籤處理類中的定義是 :若是參數不爲空,就執行標籤體中的內容
        <it:myTag4/>   //標籤體中也是一個自定義的標籤,這個標籤的意思是 : 執行我,後面的標籤就不執行了
    </it:myTag4>

    <it:myTag1/>  //輸出hello one! 

<%
     request.setAttribute("xxx", "zhangSan");
%>
    <it:myTag2>
       ${xxx }   //標籤體中的這個EL表達式會到標籤處理類中執行。
    </it:myTag3>
    <it:myTag2>
       我是張三的大哥  //標籤體中的內容也能夠是字符串
    </it:myTag3>

  </body>
</html>

補充知識1: 在web.xml中配置錯誤頁面

<error-page>
    <error-code>404</error-code>
    <location>/error404.jsp</location>
  </error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/error500.jsp</location>
  </error-page>
  <error-page>
    <exception-type>java.lang.RuntimeException</exception-type>
    <location>/error.jsp</location>
  </error-page> 
當出現404時,會跳轉到error404.jsp頁面;
當出現RuntimeException異常時,會跳轉到error.jsp頁面;
當出現非RuntimeException的異常時,會跳轉到error500.jsp頁面。

補充知識 2 :javaweb四大域對象與JSP九大內置對象

PageContext(page域)、ServletRequest(request域)、HttpSession(session域)、ServletContext(application域)。page域、request域、session域、application域這幾個詞代表的是域的範圍。 。Sevlet只能使用後三個域。JSP能使用所用的域。 簡單來講, 域對象簡單說就是能在Servlet之間(page域使用在JSP頁面中)傳遞參數,由於要下降耦合度,因此咱們建立的每一個Servlet之間都是不能相互交流的,能夠說,域對象是串聯起多個Servlet的線,能夠爲多個Servlet之間的交流傳遞數據,這就顯得比較重要。域對象內部有個Map,用來存取數據。

全部的域對象都有以下的方法:

java void setAttribute(java.lang.String name, java.lang.Object o) //保存值 
              Stores an attribute in this request. java.lang.
Object getAttribute(java.lang.String name) //獲取值 
              Returns the value of the named attribute as an Object, or null if no attribute of the given name exists. 
void removeAttribute(java.lang.String name) //移除值 
              Removes an attribute from this request.`

這四個域的範圍不一樣:

  • PageContext : 這個範圍是最小的,在JSP中只能在同一個頁面中傳遞傳遞參數。
  • HttpServletRequest:一個請求建立一個request對象,因此在同一個請求中能夠共享request,例如一個請求從AServlet轉發到BServlet,那麼AServlet和BServlet能夠共享request域中的數據;
  • HttpSession:一個會話建立一個HttpSession對象,同一會話中的多個請求中能夠共享session中的數據;瀏覽器不關,不會死亡,在一個瀏覽器不關閉的狀態下,再打開相同的瀏覽器,仍是不會死亡,可是打開不一樣的瀏覽器就不能夠訪問 。
  • ServletContext:範圍最大的一個域 ,一個應用只建立一個ServletContext對象,因此在ServletContext中的數據能夠在整個應用中共享,只要不啓動服務器,那麼ServletContext中的數據就能夠共享;服務器不關,就不會死亡

須要注意的是,PageContext可以代理其餘三個域,即,PageContext實現其餘三個域的存取操做

JSP的九大內置與上述的四大域對象能夠說沒有太大的關係。內置對象是JSP已經定義好的對象,這些對象中有的是Servlet中類的實例化對象,有點的io中類的實例化對象等等 。都是jSp中可能經常使用到的類 。 須要注意的是,pageContext這個內置對象一個頂九個,能夠獲取其餘八個內置對象。也就是說,一個pageContext內置對象就能夠實現其餘八個內置對象的功能 。

相關文章
相關標籤/搜索