现在的Java Web开发已经很少使用JSP脚本了,业务逻辑都交给Servlet处理,JSP只负责显示视图,所以接下来的内容就对JSP脚本不做叙述了。。。
JSP概述
JSP全名为Java Server Page,是为了简化Servlet的工作而出现的替代品。JSP是一种基于文本的程序,其特点是HTML与Java程序共同存在。执行时JSP会被Tomcat自动编译,编译后的JSP和HttpServlet一样,都是javax.servlet.Servlet接口的子类,因此JSP是另一种形式的Servlet。
JSP工作原理
JSP是一种Servlet,但是与HttpServlet的工作方式不太一样。HttpServlet是先由源代码编译为class文件后部署到服务器下的,先编译后部署。而JSP则是先部署源代码后编译为class文件的,先部署后编译。JSP会在客户端第一次请求JSP文件时被编译为HttpJspPage类(接口Servlet的一个子类)。该类会被服务器临时存放在服务器工作目录上面。以后客户端访问这个JSP时,服务器不再编译这个JSP文件,而是直接调用已有的class文件进行响应。所以客户端第一次请求时会感觉比较慢,因为JSP需要编译,而之后速度就快多了。但如果JSP被修改,或者class文件被删除,Tomcat能够检测到,Tomcat会在下一次请求时重新编译JSP,而不需要重启Tomcat。(这种自动检测功能是默认的,可以在web.xml中关闭)
JSP生命周期
JSP会被容器转译为Servlet源代码,自动编译为.class文件,载入.class文件,然后生成Servlet对象,如图:
JSP也是Servlet,运行时只有一个实例。和Servlet一样,JSP实例化,销毁时也会调用Servlet的init()方法与destroy()方法。另外JSP还有自己的初始化方法与销毁方法_jspInit()与_jspDestroy()。Servlet的init方法和destory()方法会相应的调用_jspInit()与_jspDestroy()方法。注意到这两个方法名称前有个下划线,表示这些方法时由容器转译时维护,所以你不能重写这些方法。如果你想对JSP做些初始化工作或收尾工作,你可以重写jspInit()方法或jspDestory方法。
JSP指令
JSP指令的主要目的,在于指示容器将JSP转译为Servlet源代码时,一些必须遵守的信息,JSP指令的语法如下所示:
<%@ 指示类型 [属性="值"]* %>
在JSP中有三种常见的指令:page、include 与 taglib。page指令告知容器如何转译目前的JSP网页。include指令告知容器将别的JSP页面包括进来进行转译。taglib指令告知容器如何转译这个页面中的标签库。
-
page指令
page指令是最常用的指令,可以声明JSP页面的属性等。JSP指令的多个属性可以写在一个page指令里,也可以写在多个page指令里。每个属性只能出现一次,否则出现编译错误。(import属性除外)。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
page指令的常见的属性:
- language:指明解释该JSP时采用的语言。一般为Java语言。默认为Java语言。
- import:指明编译该JSP时继承哪个类。
- session:指明该JSP是否内置Session对象。true则内置,false不内置。默认为true。
- autoFlush:是否允许缓存。如果为true,则使用out.println()等方法输出字符不会立刻到达服务器,而是暂时存在缓存里,缓存满时或者程序执行完毕或者执行out.flush()操作时才到客户端。默认为true。
- buffer:指定缓存大小。当autoFlush设为true时才有效,例如:<%@ page buffer="10kb" %>。
- isThreadSafe:指定是否线程安全。如果为true,则运行对个线程同时运行该JSP,否则只运行一个线程,其余线程等待。默认为false。
- isErrorPage:指定该页面是否为错误处理页面。如果为true,则该JSP内置有一个Exception对象,可直接使用,否则没有,默认为false。
- errorPage:指明一个错误显示页面。如果该JSP程序抛出了一个未捕捉的异常,则转到errorPage指定的页面。errorPage指定的页面通常是isErrorPage属性为true,且内置的Exception对象为未捕捉的异常。
- contentType:客户端浏览器根据属性判断文档类型。
- info:指明JSP的信息。该信息可以通过Servlet.getServletInfo()方法得到。
- trimDirectiveWhitespaces:是否去掉指令前后的空白字符。默认为fasle。
-
include指令
include指令比较简单,只有一种形式:<%@ inlcude file="relativeURL" %>。relativeURL是另一个JSP文件或HTML文件的路径。例如,网站内所有网站均有一个统一风格的导航栏head.jsp与页脚版权的foot.jsp,就可以使用include指令。include指令可以实现页面的区块化。
<%@include file="head.jsp"%> <h2>Hello World</h2> <%@include file="foot.jsp"%>
-
taglib
JSP支持标签技术,使用标签功能能够实现视图代码重用,很少量的代码就能够实现很复杂的显示效果。taglib指令用来指明JSP页面内使用的JSP标签库。taglib指令有两个属性,uri为类库的地址,prefix为便签的前缀。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>book</title> </head> <body> <c:forEach var="book" items="books"> <h2>${book.name}</h2> </c:forEach> </body> </html>
JSP九大隐式对象
Servlet和JSP中输出数据都需要使用out对象。Servlet中的out对象是通过response.getWriter()得到的。而JSP中没有定义out对象,却可以直接使用。这是因为out是JSP的内置对象。JSP中一共内置了9个内置对象,分别为out、request、response、config、session、application、page、pageContent、exception。
- out为javax.servlet.jsp.JspWriter类的实例、服务器向客户端输出的字符内容可以通过out对象输出。
- request为javax.servlet.ServletRequest类的实例,代表着客户端的请求。
- response为javax.servlet.ServletResponse类的实例,代表着服务器的响应。
- config是java.servlet.ServletConfig类的实例。ServletConfig封装了配置在web.xml中初始化JSP的参数。
- session是java.servlet.http.HttpSession的实例。Session是记录客户会话状态的机制。
- application是java.servlet.ServletContext的实例。application封装了JSP所在的Web应用程序的信息。
- page是java.servlet.jsp.HttpJspPage类的实例、page对象代表当前JSP页面,是JSP编译后的Servlet类的对象。相当于java语言中的this。
- pageContext为java.servlet.jsp.PageContext的实例,PageContext对象代表当前页面编译后的内容。通过PageContext对象能够得到JSP中的资源。
- exception为java.lang.Exception类的对象,Exception对象封装了JSP抛出的异常,要使用exception隐藏对象,需要设置<%@ isErrorPage="true" %>。exception通常用来处理错误页面。
EL表达式
JSP可以使用EL表达式从page、request、session、application域中获取属性,还可以进行简单的运算和判断,这样可以大大减少JSP页面上Scriptlet的使用。
EL表达式的11个隐式对象
- pageContext对应于JSP页面中的pageContext对象。
- pageScope代表page域中保存属性的Map对象。
- requestScope代表request域中保存属性的Map对象。
- sessionScope代表session域中保寸属性的Map对象。
- applicationScope代表application域中保存属性的Map对象。
- param代表保持了所有请求参数的Map对象。
- paramValues代表保存了所有请求参数的Map对象,他对于某个请求参数,返回的是一个String类型的数组。
- header代表一个保存类所有头字段的Map对象。
- headerValues代表一个保存类所有头字段的Map对象,返回String类型数组。
- cookie表示类一个保存类所有cookie的Map对象。
- initParam表示一个保存了所有Web应用初始化参数的Map对象。
-
pageContext对象
EL表达式中的pageContext隐式对象与JSP页面中的pageContext对象相对应,EL表达式可以通过pageContext隐式对象访问其它JSP隐式对象,如访问request、response对象属性时,可以使用表达式$ {pageContext.response.contentType}等。
-
代表特定域属性的隐式对象(共4个)
EL表达式中的pageScope、requestScope、sessionScope和applicationScope四个隐式对象分别用于访问JSP页面的page、request、session、application四个域中的属性。在EL表达式中也可以不使用这些隐式对象来指定查找域,而是直接引用这些域中的属性名称。例如,表达式$ {userName}就会在page、request、session、application这四个作用域内按顺序依次查找userName属性,直到找到为止。
-
代表请求参数的隐式对象(2个)
EL表达式中的隐式对象param和paramValues用于获取客户端访问JSP页面时传递的请求参数的值,由于HTTP协议允许使用一个请求参数名出现多次,即一个请求参数可能会有多个值,所以,EL表达式提供了param和paramValues这两个隐式对象来分别获取请求参数的某个值和所有值。Param隐式对象用于返回一个请求参数的某个值,如果同一个请求参数有多个值,则返回第一个参数的值。paramValues隐式对象用于返回一个请求参数的所有值,返回结果为该参数的所有值组成的字符串数组,例如表达式${paramValues.username[0]}用于返回数组中第一个元素的值。
-
代表HTTP请求消息的隐式对象(2个)
EL表达式中的隐式对象header和headerValues用于获取客户端访问JSP页面时传递的请求头字段的值。由于HTTP协议允许一些请求头字段出现多次,即一个请求头字段可能会有多个值,所以,EL表达式提供了header和headerValues两个隐式对象分别用于获取请求头字段的某个值和所有值。header隐式对象返回一个请求头字段的某个值,如果同一个请求头字段有多个值,则返回第一个值,例如,使用表达式${header.referer}可以非常方便的获取referer请求头字段的值。headerValues隐式对象用于返回一个请求头字段所有值组成的字符串数组。
-
cookie隐式对象
EL表达式中的隐式对象cookie是一个代表所有Cookie信息的Map集合,Map集合中元素的关键字为各个Cookie的名称,值则为对应的Cookie对象。使用cookie隐式对象可以访问某个Cookie对象,这些Cookie对象则是通过调用HTTPServletRequest.getCookies()方法得到的,如果多个Cookie共用一个名称,则返回Cookie对象数组中的第一个Cookie对象。例如,要访问一个名为userName的Cookie对象,可以使用表达式${cookie.userName}。
-
initParam隐式对象
EL表达式中的initParam是一个代表Web应用程序中的所有初始化参数的Map对象,每个初始化参数的值是ServletContext.getInitParameter(String name)方法返回的字符串。
EL表达式获取属性
EL表达式默认是以page、request、session、application的顺序来寻找EL中指定的属性的。EL通过[ ]运算符和 . 运算符来获取指定的属性。
- 如果使用 . 运算符,则左边可以是JavaBean、Map对象或自定义对象。
- 如果使用[ ]运算符,则左边可以是JavaBean、Map、数组、List对象或自定义对象。
简单来说就是有下标的使用[ ],没下标的都用 。
${book.name}
EL表达式运算
EL表达式支持简单的运算,包括加(+)、减(-)、乘(*)、除(/或者div)、取余(%或者mod)、三目运算符等,例如:
${1 + 2}<!--输出3-->
${1 == 2 ? 3 : 4}<!--输出4-->
EL表达式支持简单的比较运算,包括大于(> 或 gt),小于(< 或 lt),等于(== 或 eq),不等于(!= 或 ne),大于等于(>= 或 ge),小于等于(<= 或 le)等,例如:
${1 < 2}<!--输出true-->
${5 >= 2}<!--输出false-->
EL表达式还支持逻辑运算,且(&& 或 and),或(|| 或 or),否(! 或 not),例如:
${1 < 2 && 5 >= 2}<!--输出false-->
还可以结合JSTL标签库判断域中某值是否为空,然后可以在界面上显示不同的信息,例如:
<c:if test="${empty user}">
<h3>请先登入</h3>
</c:if>
<c:if test="${not empty user}">
<h3>${user.name},欢迎您</h3>
</c:if>
自定义EL函数
支持自定义EL函数的功能,如果我们想使用EL表达式连接字符串,你可能会想到使用${ str1 + str2 },但是这是错的,因为EL表达式中的 + 只代表加法,要实现该功能,你可以自定义EL函数。
第一步:编写类,这个函数必须是公开的(public)且是静态方法(static),例如:
package Utils;
public class Util {
public static String strcat(String str1, String str2) {
return str1 + str2;
}
}
第二步:编写标签程序库描述(Tag Liabrary Descriptor)文件,这个文件是一个XML文件。
<?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>util</short-name>
<uri>http://kindleheart.com/util</uri>
<function>
<name>strcat</name>
<function-class>Utils.Util</function-class>
<function-signature>
java.lang.String strcat(java.lang.String,java.lang.String)
</function-signature>
</function>
</taglib>
第三步:直接使用该标签,例如:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="util" uri="http://kindleheart.com/util" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>自定义EL函数</title>
</head>
<body>
<%
String str1 = "今天天气";
String str2 = "真好";
request.setAttribute("str1", str1);
request.setAttribute("str2", str2);
%>
${util:strcat(requestScope.str1, requestScope.str2)}
</body>
</html>
网页显示结果: