JSP技术
一.概述
1.JSP
- JSP(Java Server Pages):是一种动态网页技术,是Servlet更高级别的扩展
- JSP文件中,HTML代码与Java代码共同存在,HTML用于实现静态内容的显示,Java用于动态内容的显示
- JSP文件会被Web服务器编译成一个Servlet,用于处理各种请求,然后将生成的网页响应给客户端
2.运行原理
- 图示
- 过程
- 客户端发送请求,请求访问JSP文件
- JSP容器将JSP文件转换成Java源文件(一个Servlet类),转换过程中发现语法错误会中断并向服务器和客户端返回出错信息,JSP容器本身也是一个Servlet
- 转换成功后,JSP容器继续将这个Java源文件编译成相应的字节码文件(.class),该字节码文件就是一个Servlet,Servlet容器会像处理其他Servlet一样来处理它
- Servlet容器根据这个字节码文件创建出一个Servlet实例,并执行jspInit()方法完成初始化,这个jspInit()在Servlet的整个生命周期中只会执行一次,这个Servlet实例会常驻在内存中
- JSP容器执行jspService()方法来处理客户端的请求,对于每个请求都会创建一个新的线程来处理它,多个客户端请求就会创建多个线程,每个线程对应每个客户端
- 如果JSP文件被修改,服务器根据设置会决定是否对该文件进行重新编译,如果重新编译则编译后的结果会取代内存中常驻的Servlet实例
- 由于系统资源不足等原因,JSP容器可能会以某种不确定的方式将Servlet实例从内存中移除,此时会调用jspDestroy()方法,然后Servlet实例被加入“垃圾收集”处理
- 请求处理完成后,响应的结果先由JSP容器接收,然后将HTML格式的信息发送回客户端
二.JSP语法
1.JSP脚本元素
- JSP脚本元素是指嵌套在
<%
和%>
中的一条或多条Java代码,通过JSP脚本元素来将Java代码嵌入HTML代码中,所有可以执行的Java代码都可以通过JSP脚本执行 - JSP脚本元素分为3种类型,JSP Scriptlets、声明标识、JSP表达式
- JSP Scriptlets
- JSP Scriptlets是一段Java代码,可以定义变量、控制流程语句等
- 可以使用JSP内置对象在页面输出内容
- JSP Scriptlets中的代码位于JSP文件转换成的Servlet类中的jspService()方法,所以不能在里边定义方法,而在里边定义的变量都是局部变量
- 除了定义局部变量还可以处理请求和访问Session会话等
- 范例
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>第一个jsp页面</title> </head> <body> <h1>my first jsp page</h1> <% int a = 10; String s = "abc"; %> </body> </html>
- 声明标识
- 在JSP Scriptlets中可以定义属性也可以输出内容,但是不能定义方法,但是使用声明就能实现方法的定义和成员变量的定义
- 声明标识中的内容是位于JSP转换成的Servlet类中的成员位置,所以可以定义成员方法、成员变量
- 在声明标识中定义的方法和变量可以被整个JSP页面访问,所以通常使用该标识定义整个JSP页面需要引用的变量或方法
- 语法格式
<%! 定义成员变量或方法 %>
- 在声明标识中是用于定义变量和方法的,不能在此处用于输出内容
- JSP表达式
- JSP表达式用于向页面输出信息
- 语法格式
<%= expression %>
expression可以是java语言中任意完成的表达式,最终运算结果会被转化成字符串
- 实例
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>jsp页面</title> </head> <body> <%! int a = 50; int b = 20; %> <%= a+b%> </body> </html>
2.JSP注释
- JSP文件中如果需要注释Java代码,那么可以在Java代码处直接使用Java的注释语法
- JSP文件中如果需要注释HTML代码,那么可以在HTML代码处使用HTML的注释语法
- 使用HTML的注释后虽然在网页中看不到注释后的内容,但是通过查看源代码还是能看到注释信息的,这是因为Tomcat编译JSP文件时会将HTML注释当成普通文本发送给客户端
- JSP提供了一种隐藏注释,使用这种注释的内容不会在网页源代码中显示,语法格式如下
<%-- 注释内容 --%>
3.JSP指令
- 为了设置JSP页面的一些信息,可以使用JSP指令,JSP中定义了page、include、taglib三种指令,每种指令都定义了各自的属性
- page指令
- 格式
<%@ page 属性名1="属性值1" 属性名2="属性值2" %>
- page常用属性如下
注意以上属性中,只有import属性可以出现多次,一个import也可以引入多个类,中间用英文逗号隔开,而其他属性只能出现一次
属性名 |
取值范围 |
描述 |
language |
java |
指定jsp页面的脚本语言,默认为java |
import |
任何包名、类名 |
指定jsp转换成Servlet类时需要引入的包或者类 |
session |
true、false |
指定jsp是否内置Session,如果true则内置Session对象,否则没有内置Session对象,默认为true |
isErrorPage |
true、false |
指定该页面是否为错误处理页面,如果是true则该jsp页面内置一个Exception对象exception,可以直接使用,默认取值为false |
errorPage |
某个jsp页面的相对路径 |
指定一个错误页面,如果该JSP页面抛出一个未捕获的异常,则转到这个属性指定的页面,指定的页面的isErrorPage取值为true并且内置的Exception对象就是这个未捕获的异常 |
contentType |
有效的文档类型 |
指定当前jsp页面的MIME类型和字符编码,例如HTML格式为 、纯文本格式为 、JPG图像为 、GIF图像为 、Word文档为 |
pageEncoding |
当前页面 |
指定页面的编码格式 |
- 范例
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="java.util.*" %> <%@ page import="java.awt.*","java.util.*" %>
- include指令
- 格式
<%@ include file="被包含的文件路径" %>
- include指令用于包含另一个jsp页面,只有一个file属性,用于指定被包含的页面,使用的是相对地址,不以/开头
- 注意在被包含的页面中需要将html、body等标签去掉,因为当前页面已经存在这些标签
- taglib指令
- 格式
<%@ taglib prefix="tagPrefix" uri="tagURI" %>
- taglib指令用于标识该页面所使用的标签库,同时引用标签库并指定标签的前缀,引入标签库后就可以通过前缀来引用标签库中的标签
- 属性说明
- prefix:用于指定标签前缀,不能命名为jsp、jspx、java、sun、servlet和sunw
- uri:指定标签库文件的存放位置
- 引用JSTL中核心标签库的范例
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
4.动作元素
- 包含文件元素
- 语法
<jsp:include page="URL" flush="true|false" />
page:需要引入的页面相对路径
flush:是否将页面内容刷新到客户端,默认为false
- 原理
- 页面1使用该动作元素将页面2包含,当浏览器第一次请求页面1时,Web容器会先编译页面2,然后将编译结果包含在页面1中,之后再编译页面1,最终将两个页面组合的结果回应给浏览器
- flush属性为flase时,服务器会等页面完全实现后才返回给浏览器显示,而true时当缓存累积到一定数据时会先将部分页面返回给浏览器显示并等待后续内容,即是否自动刷新页面
- 与include指令的区别
- include的file属性不支持jsp表达式,而<jsp:include>的page属性支持jsp表达式
- include指令是将被包含的页面插入当前页面中再编译然后返回给浏览器,而<jsp:include>是将被包含的文件先编译返回给浏览器后再回到当前页面继续执行当前页面的内容
- include指令包含页面是将两个页面合并在一起所以两个页面中的变量名和方法还有一些产生冲突的html标签不能产生冲突,而使用<jsp:include>时,因为每个文件是单独编译的,所以没有冲突问题
- 请求转发元素
- 语法
<jsp:forward page="relativeURL" />
page:指定将请求转发到的页面的相对路径
- 原理
- <jsp:forward>元素可以将当前请求转发到其他的Web资源,如HTML页面、JSP页面、Servlet等
- 执行请求转发后当前页面将不再执行,但是地址栏中的资源路径不会改变
三.JSP隐式对象
1.九大隐式对象
对象名 |
类型 |
说明 |
out |
javax.servlet.jsp.JspWriter |
用于页面输出 |
request |
javax.servlet.http.HttpServletRequest |
请求信息 |
response |
javax.servlet.http.HttpServletRequest |
响应信息 |
config |
javax.servlet.ServletConfig |
配置信息 |
session |
javax.servlet.http.HttpSession |
会话信息 |
application |
javax.servlet.ServletContext |
站点信息 |
page |
java.lang.Object |
该页面转换成Servlet的实例 |
pageContext |
javax.servlet.jsp.PageContext |
JSP页面容器 |
exception |
java.lang.Throwable |
JSP中页面的异常,在错误页中起作用 |
- JSP提供了上诉九个隐式(内置)对象,是JSP默认创建的,可以直接在JSP页面中使用
- request、response、config、session、application对象的细节在前文已述
2.out对象
- out对象是javax.servlet.jsp.JspWriter类的实例,用于向页面输出内容
- out对象带有缓冲区,向页面中输出的内容会存入缓冲区中
- 只有调用了ServletResponse的getWriter()方法才会将out对象缓冲区的内容写入Servlet引擎(容器)提供的缓冲区中
- 图示如下
网络异常,图片无法展示| - 范例
代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>jsp页面</title> </head> <body> <% out.println("first line <br/>"); response.getWriter().write("second line <br/>"); %> </body> </html>
输出结果:
分析:
- out对象使用println()方法写入数据后,直到整个JSP页面结束,out对象中缓冲区的数据才写入Servlet引擎提供的缓冲区中
- 而response.getWriter().write()方法直接将数据写入Servlet引擎缓冲区中,最后按照顺序输出
3.pageContext对象
- pageContext对象是javax.servlet.jsp.PageContext类的实例对象,代表当前jsp页面的运行环境
- pageContext对象可以获取其他八个隐式对象
方法 |
说明 |
JspWriter getOut() |
获取out对象 |
Object getPage() |
获取page对象 |
ServletRequest getRequest() |
获取request对象 |
ServletResponse getResponse() |
获取response对象 |
HttpSession getSession() |
获取session对象 |
Exception getException() |
获取exception对象 |
ServletConfig getServletConfig() |
获取Config对象 |
ServletContext getServletContext() |
获取application对象 |
- pageContext对象可以设置和获取数据从而实现共享数据,但是需要指定范围
方法 |
说明 |
void setAttribute(String name,Object value,int scope) |
设置属性和对应的值,scope指定作用的范围 |
Object getAttribute(String name,int scope) |
获取属性和对应的值,scope指定从哪个范围获取 |
void removeAttribute(String name,int scope) |
从指定范围移除指定的属性 |
void removeAttribute(String name) |
从所有范围移除指定的属性 |
Object findAttribute(String name) |
从四个域中查找属性 |
- scpoe有四个取值,分别是
pageContext.PAGE_SCOPE,表示当前页面范围
pageContext.REQUEST_SCOPE,表示请求范围
pageContext.SESSION_SCOPE,表示会话范围
pageContext.APPLICATION_SCOPE,表示Web应用范围 - findAttribute()方法从四个域中查找属性时顺序是page、request、session、application
- pageContext实现了在当前页面中获取四大域对象进行数据共享的功能
4.四大域对象
域对象 |
范围 |
pageContext |
最小,表示当前页面 |
ServletRequest |
在请求转发时使用 |
HttpSession |
多次请求时使用 |
ServletContext |
最大,整个应用都能使用 |
四.EL表达式
1.概述
- JSP开发中为了获取域对象中的数据,需要书写很多Java代码,造成了页面混乱
- 使用EL表达式可以简化JSP开发中的对象引用,规范页面代码
2.EL表达式基本语法
- 格式:
${表达式}
注意:如果需要再=在页面中显示“&{”,需要在前面加上符号“\”,即“\${”,或者写成“&{&{}”
- 范例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式</title> </head> <body> <% request.setAttribute("name","张三"); %> <%--传统方式--%> <%=request.getAttribute("name")%> <%--使用EL表达式--%> ${name} </body> </html>
注意:使用EL表达式获取域对象中的数据时,如果对象中没有该数据则返回空字符串,而Java代码会返回null从而报空指针异常
3.EL访问数据
- 点运算符
.
${student.name}
- 中括号运算符
[]
${students[0]}
- 点运算符和中括号运算符主要是用来访问对象、数组、集合中的数据
- 某些属性名含有特殊符号时,只能用中括号运算符,如${student["My-Name"]}
- 点运算符和中括号运算符可以互换使用,也可以结合使用,如${users[0].userName}
- 范例
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>EL表达式</title> </head> <body> <%--访问基本数据类型--%> <%request.setAttribute("age",18);%> ${age} <%--访问自定义类型数据--%> <% Student student = new Student("liaoxiangqian",21); request.setAttribute("student",student); %> ${student.name}<%--这里实际上是使用了student的getName()方法--%> <%--访问数组类型数据--%> <% int[] array = new int[]{1,2,3}; request.setAttribute("array",array); %> ${array[0]} <%--访问List集合数据--%> <% ArrayList<String> list = new ArrayList<>(); list.add("hello"); list.add("JSP"); request.setAttribute("list",list); %> ${list} ${list[1]} <%--访问Map集合数据--%> <% HashMap<String,String> map = new HashMap<>(); map.put("map01","data01"); map.put("map02","data02"); request.setAttribute("map",map); %> ${map.map01} </body> </html>
- 注意:
- EL表达式没有空指针异常,没有索引越界异常,没有字符串拼接
- 如果出现空指针和索引越界,只会返回空字符串
- 用“+”进行字符串拼接,会抛出异常
4.EL运算符
- 算术运算符
运算符 |
说明 |
表达式 |
+ |
加 |
${10+2} |
- |
减 |
${10-2} |
* |
乘 |
${10*2} |
/(或div) |
除 |
网络异常,图片无法展示
|
|
%(或mod) |
取模 |
网络异常,图片无法展示
|
|
- 比较运算符
注意:上述比较运算符输出结果为true或false
运算符 |
说明 |
表达式 |
==(或 eq) |
等于 |
网络异常,图片无法展示
|
|
!=(或 ne) |
不等于 |
网络异常,图片无法展示
|
|
<(或 lt) |
小于 |
网络异常,图片无法展示
|
|
>(或 gt) |
大于 |
网络异常,图片无法展示
|
|
<=(或 le) |
小于等于 |
网络异常,图片无法展示
|
|
>=(或 ge) |
大于等于 |
网络异常,图片无法展示
|
|
- 逻辑运算符
运算符 |
说明 |
表达式 |
&&(或and) |
逻辑与 |
网络异常,图片无法展示
|
|
||(或or) |
逻辑或 |
网络异常,图片无法展示
|
|
!(或not) |
逻辑非 |
网络异常,图片无法展示
|
|
- empty运算符
- 格式
${empty 变量/对象}
- empty运算符用来判断一个对象或变量是否为空,这里的空是指空字符串、空指针null、空数组、空集合
- 三元运算符
- 格式
${true == false ? "a" : "b"}
- 上式中“a”或“b”就是整个表达式的返回值
- 注意:上述的运算符可以通过括号运算符
()
来改变运算优先级
5.EL隐式对象
- EL提供11个内置对象,无需创建可以直接使用
- 11个隐私对象如下
注意:不要和JSP中的隐式对象混淆,只有pageContext是相同的,其他的没有关系
对象名 |
说明 |
pageContext |
与JSP中的pageContext对象功能相同 |
pageScope |
保存page域中属性值的Map对象 |
requestScope |
保存request域中属性值的Map对象 |
sessionScope |
保存session域中属性值的Map对象 |
applicationScope |
保存application域中属性值的Map对象 |
param |
保存了所有请求参数的Map对象 |
paramValues |
保存了所有请求参数的Map对象,一个参数对应一个String类型数组 |
header |
保存了所有HTTP请求头字段的Map对象 |
headerValues |
保持了所有HTTP请求头字段的Map对象,一个字段对应一个String类型数组 |
cookie |
获取cookie的值,类型是Map |
initParam |
保存了所有Web应用初始化参数的Map对象 |
- pageContext对象
- 可以使用pageContext对象来获取JSP页面中的其他8个隐私对象
- 范例
${pageContext.request.getAttribute("username")} <%--相当于--%> <%=request.getAttribute("username")%>
- 域相关对象
- 域相关对象是指pageScope、requestScope、sessionScope、applicationScope这四个对象
- 这四个对象与JSP隐式对象中的page、request、session、application相似
- 这四个对象主要用于从它们对应的域中取出相关的属性值,而不采用默认的查找顺序去取值,如
<%--username是request域中的属性名--%> ${username}<%--这是默认的方式--%> ${requestScope.username}<%--这是用隐式对象的方式--%>
- 如果不使用隐式对象而使用默认的方式会按照page、request、session、application的顺序查找
- 访问环境信息的隐式对象
- 访问环境信息的隐式对象有param、paramValues
- 这两个对象用于获取表单信息中提交的成对参数,其中paramValues是用于获取一个参数对应的多个参数值
- 访问HTTP请求头字段的对象
- 获取HTTP请求头字段的对象有header、headerValues
- 这两个对象用于根据头字段的参数,获取参数对应的值,其中headerValues是用于获取一个头字段的多个值
- cookie对象
- cookie对象用于获取Cookie中的信息,然后存入一个Map集合中,其中键是Cookie的名称,值为Cookie对象
- 范例
${cookie.username}<%--返回的是一个Cookie对象,在页面中显示的是这个Cookie对象的地址--%> ${cookie.username.name}<%--返回的是Cookie对象的name属性的值--%> ${cookie.username.value}<%--返回的是Cookie对象的value属性的值--%>
- initParam对象
- initParam对象用于获取Web应用初始化参数的值
- 范例
在web.xml文件中存在一个初始化参数:
<context-param> <param-name>author</param-name> <param-value>liaoxiangqian</param-value> </context-param>
jsp文件中使用initParam对象获取author这个参数的值
五.JSTL
1.概述
- 使用JSTL可以取代传统JSP开发中嵌入Java代码的做法,提高程序可维护性
- 需要使用taglib指令定义引用标签库和访问前缀
- 包含5类标准标签库,分别是核心标签库、国际化/格式化标签库、SQL标签库、XML标签库、函数标签库
- 使用JSTL需要下载相关Jar包然后导入项目
2.核心标签库
- 使用核心标签库的taglib指令如下
<%@ taglib prefix="c" url="http://java.sun.com/jsp/jstl/core" %>
- <c:out>标签
<%--无标签体--%> <c:out value="value" [default="defaultValue"] [escapeXml="{true|false}"] /> <%--有标签体--%> <c:out value="value" [escapeXml="{true|faslse}"]> defaultValue </c:out>
- <c:out>标签用于向页面输出信息
- value指定输出的文本内容,也可以是EL表达式
- default指定value为null时的输出内容,是可选的属性(方括号属性是可选的),
- escapeXml属性用于指定特殊字符是否进行HTML编码后输出,默认为true
- <c:remove>标签
<c:remove var="name" [scope="范围"]>
- <c:remove>标签用于移除指定JSP范围内的变量
- var指定要移除的变量名
- scope指定范围,可选值有page、request、session、application,默认为page
- <c:if>标签
<%--没有标签体--%> <c:if test="testCondition" var="result" [scope="{page|request|session|application}"] /> <%--有标签体--%> <c:if test="testCondition" var="result" [scope="{page|request|session|application}"]> body content </c:if>
- <c:if>标签用于条件判断
- test属性是需要判断的逻辑表达式
- var用于指定逻辑表达式的变量名字
- scope指定var的作用范围,默认为page
- <c:choose>标签、<c:when>标签、<c:otherwise>标签
<c:choose> <c:when test="testCondition"> body content </c:when> <c:when test="testCondition"> body content </c:when> <c:otherwise> body content </c:otherwise> </c:choose>
- 这三个标签类似多重if语句
- 当when中的test属性返回值是true时对应的语句执行,只有所有的when都不匹配时才会执行otherwise中的内容
- <c:forEach>循环标签
<c:forEach [var="varName"] items="collection" [varStatus="varStatusName"] [begin="begin"] [end="end"] [step="steo"] > body content </c:forEach>
- <c:forEach>循环标签用于迭代集合的操作
- var表示当前迭代到的元素保存到page中的名称
- items指定迭代的集合
- varStatus用于指定将当前迭代状态信息对象保存到page中的名称
- begin,end用于指定迭代始末
- step指定迭代步长