《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内置对象就能够实现其余八个内置对象的功能 。

相关文章
相关标签/搜索