目录
一.什么是 JSP
JSP全称是 java servlet pages,它和 servlet 技术一样,都是Sun公司定义的一种用于开发动态web页面的技术
- 为什么jsp也是一种动态web资源的开发技术呢?
写jsp,虽然就像是在写HTML,但是jsp允许在页面中编写java代码,并且允许开发人员在页面中获取request、response等 web 开发常用对象,实现与浏览器的交互,所以jsp也是一种动态web资源的开发技术;
例子:输出当前时间
Date date = new Date();
//这个out对象是可以直接使用的,它是JspWriter的实例
out.write(date.toLocaleString());
二.JSP 调用和运行原理(简略版)
浏览器访问 jsp 页面时,Web 服务器是如何调用并执行一个jsp页面的?
首先知道,我们访问服务器的资源时,无论访问的是什么?比如访问的是HTML、jsp,其实我们访问的都是一个servlet,而不是真正的jsp、html ,因此,我们访问 jsp 其实就是去访问 servlet ;
Jsp在被访问的时候,服务器会将它 翻译为 servlet,转换后的servlet,被保存在服务器目录下的work/项目名/apache/…
提问:
- web 服务器在执行jsp页面时,是如何把jsp页面中的HTML排版标签输出给浏览器的?
这里其实是 jsp 对应的 servlet 的功劳,我们知道服务器会将jsp转为一个servlet 对象,我们去访问jsp的时候,实际访问到的也 是这个servlet对象;
在这个servlet对象对应的类中。它是通过out.writer()语句将 jsp 中的 HTML 语句,原封不动的打给浏览器;遇到<%java代 码%>,就直接执行;
- jsp页面中的java代码,服务器是如何执行的?
是原封不动的,代码最后都在在servlet类中执行了;
- Web服务器在调用jsp时,会给jsp提供一些什么java对象?
会提供许多对象;request、response、out、application、session等这些对象,在jsp中是直接可以使用的,因为在jsp对应的 servlet类中已经提供好了;
如果想要获得jsp对应的servlet类的对象的自身,使用page,也是它内置提供的;
上面的几个问题,其实都可以翻看对应的 servlet 类的源码,,找到答案;
三.JSP语法
JSP模板元素
Jsp 页面中的 HTML 内容称之为 jsp 模板元素。Jsp的模板元素定义了网页的基本骨架,即定义了页面的结构和外观;
JSP脚本表达式
语法:<%= xxxx %> 输出代码块,用于向JSP页面输出内容,只能写在一行;用out.print(...),将数据输出给客户端。
Jsp脚本片段
语法: <%多行java代码%>
java代码块,可以在其中编写任何java代码,该代码块最终被原封不动的放到 servlet 的 _jspService 方法中。
Jsp声明
语法:<%! java代码 %> 声明代码块,用于声明全局变量或方法,但是一般没啥人使用这个技术;
Jsp注释
格式:<%-- 注释信息 --%>
Jsp指令
jsp指令 是为 jsp引擎 而设计的。它们并不直接产生任何可见输出,而只是告诉引擎如何处理jsp页面中的其余部分。
在jsp2.0规范中共定义3个指令:
page指令 Include指令 taglib指令
四.JSP 指令-Page指令
page指令
用于定义jsp页面的各种属性,无论page指令出现在jsp页面中的什么地方,它作用的都是整个jsp页面;
为了保持程序的可读性和遵循良好的编程习惯,page指令
最好是放在整个jsp页面的初始位置。
Jsp2.0规范中定义的 page指令
的完整语法:
五.JSP 指令-Include指令
include 指令用于引入 jsp 页面,如果使用 include 引入其他 jsp 页面,那么 jsp 引擎将把这两个jsp翻译为一个servlet,所以,include指令引入通常也称为 静态导入;
<%@include file="footer.jsp"%>
被导入的页面,不要包含HTML语句了,否则页面将不是一个完成的页面,会出现两个 <head> 这样的标签;
动态包含:
语法: request.getRequestDispatcher("url").include(request,response);
这是在运行时包含,会将 每一个 jsp 都对应生成一个servlet,也就是最终生成不止一个 servlet
我们一般选择静态导入,性能高 ;
六.JSP 指令-taglib指令
看下面的标签库.
七.JSP 动作
JSP中有3个动作标识:
1.<jsp:include>
动态包含,用于将一个JSP页面包含在当前页面中。所谓动态包含指的是,被包含页面经编译后呈现在当前页面中,包含页面与 被包含页面各自生成一个Servlet。
2.<jsp:forward>
用于页面跳转(服务器内部跳转)。
3.<jsp:param>
与<jsp:forward>标识结合使用,用于在页面跳转时传递请求参数
// 动态包含
<jsp:include page=""> </jsp:include>
//跳转页面
<jsp:forward page="">
// 负责带数据到另一个JSP页面中,value的值,可以是脚本表达式
// 一般和包含include便签一起使用
<jsp:param name="" value=""></jsp:param>
// 将数据带到xxx.jsp 中
<jsp:include page="xxxx.jsp">
<jsp:param name="xxx" value="xxx"/>
</jsp:include>
八.JSP九大内置对象
JSP引擎 在调用 JSP 对应的 _JspServlet 时,会 传递或创建 9个与WEB开发相关的对象供_JspServlet使用。
JSP技术的设计者为了方便web开发人员在编写JSP页面时,获得这些对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这9个变量就可以快速的获得这9大对象的引用;
这九大隐式对象就是:response、request、session、application(ServletContext)、page (jsp对象自身)、config(servlertConfig)、exception、out(JspWriter)、PageContext
前面7个,在学JSP之前,都已经学过了,这里重点讲最后两个;
Out隐式对象
作用:Out隐式对象
用于向客户端发送文本数据;
Out对象
是通过调用 PageContext
对象的 getOut()
方法返回的,其作用和用法与servletResponse.getWriter
方法返回的 PrintWriter
对象非常相似。
九.JSP九大内置对象-PageContext对象
是jsp技术中最重要的一个对象,它代表jsp页面的运行环境;生命周期是一个jsp页面;
这个对象自身就封装可其他8大隐式对象的引用;(这个最厉害了)
它自身还是一个域对象,可以用来保存数据;
这个对象中,还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其他资源、检索其他域对象中的属性;
1.通过PageContext
对象获得其他对象
通过 getXXX()
方法;
PageContext
对象中包含其他8大隐式对象的引用;
<%
pageContext.getPage();
pageContext.getRequest();
pageContext.getResponse();
pageContext.getSession();
pageContext.getServletContext();
pageContext.getServletConfig();
pageContext.getOut();
pageContext.getException();
%>
2.PageContext域的方法
javaweb四大域
获取数据
pageContext.getAttribute(String) ;
设置数据
pageContext.setAttribute(String,String);
移除数据
pageContext.removeAttribute(String);
//设置和获取其他域的数据
获取id对应的域的数据
pageContext.getAttribute(String,id) ;
pageContext.setAttribute("name","joe",PageContext.REQUEST_SCOPE);
设置id对应的域数据
pageContext.setAttribute(String,String,id);
移除id对应的域数据
pageContext.removeAttribute(String,id);
PageContext查找属性的方法
pageContext.findAttribute(String)
用于查找某个属性的属性值,它会依次从page、request、session、application四个域,从小到大的查找,在某个域中查到数据,即刻返回这个数据,不会再继续查下去。如果四个域都没查到,则返回null;
这个方法,便利于我们可以在jsp中直接使用这个方法来查找数据,而不要去关注数据在哪一个域中;
3.引入和跳转到其他资源
PageContext
类中定义了一个forward
方法和两个include
方法来分别简化和替代RequestDispatcher(...).forward
方法和RequestDispatcher(...).include
方法。
原始的操作
:
// forward
request.getRequestDispatcher("/xxx").forward(request, response);
// include
request.getRequestDispatcher("/xxx").include(request, response);
使用pageContext
简化:
// forward
pageContext.forward("/xxx");
// include
pageContext.include("/xxxx");
方法接受的资源如果以 / 开头,/ 代表当前web应用;
十.JSTL标签
1、什么是JSTL?
JSTL JSP Standard Tag Library 标准标签库
JSTL是apache对EL表达式的扩展(也就是说JSTL依赖EL),JSTL是标签语言!JSTL标签使用以来非常方便,它与JSP动作标签一样,只不过它不是JSP内置的标签,需要我们自己导包,以及指定标签库而已!
2、JSTL标签库:
JSTL一共包含四大标签库:
- core:核心标签库,我们学习的重点;
- fmt:格式化标签库,只需要学习两个标签即可;
- sql:数据库标签库,不需要学习了,它过时了;
- xml:xml标签库,不需要学习了,它过时了。
3、使用taglib指令导入标签库:
除了JSP动作标签外,使用其他第三方的标签库都需要:
- 导包;
- 在使用标签的JSP页面中使用taglib指令导入标签库;
下面是导入JSTL的core标签库:
<%@ taglib prefix="c"uri="http://java.sun.com/jstl/core" %>
- prefix="c":指定标签库的前缀,这个前缀可以随便给值,但大家都会在使用core标签库时指定前缀为c;
- uri="http://java.sun.com/jstl/core":指定标签库的uri,它不一定是真实存在的网址,但它可以让JSP找到标签库的描述文件;
4、core标签库常用标签:
(1)out和set标签
<c:out value=”aaa”/> 输出aaa字符串常量
<c:out value=”${aaa}”/> 与${aaa}相同
<c:out value=”${aaa}” default=”xxx”/> 当${aaa}不存在时,输出xxx字符串
<c:set var=”a” value=”hello”/> 在pageContext中添加name为a,value为hello的数据。
<c:set var=”a” value=”hello” scope=”session”/> 在session中添加name为a,value为hello的数据。
(2)remove标签
<%
pageContext.setAttribute("a","pageContext");
request.setAttribute("a","session");
session.setAttribute("a","session");
application.setAttribute("a","application");
%>
<c: remove var="a"/> 删除所有域中name为a的数据!
<c:remove var="a" scope=”page”/> 删除pageContext中name为a的数据
(3)url标签:该标签会在需要重写URL时添加。
<c:url value="/"/> 输出上下文路径:/项目名/
<c:url value="/" var="a" scope="request"/> 把本该输出的结果赋给变量a。范围为request
<c:url value="/AServlet"/> 输出:/项目名/AServlet
输出:/项目名/AServlet?username=abc&password=123 如果参数中包含中文,那么会自动使用URL编码!
<c:url value="/AServlet">
<c:param name="username" value="abc"/>
<c:param name="password" value="123"/>
</c:url>
(4)if标签:
if标签的test属性必须是一个boolean类型的值,如果test的值为true,那么执行if标签的内容,否则不执行。
<c:set var="a" value="hello"/>
<c:if test="${not empty a }">
<c:out value="${a }"/>
</c:if>
(5)choose标签:
choose标签对应Java中的if/else if/else结构。when标签的test为true时,会执行这个when的内容。当所有when标签的test都为false时,才会执行otherwise标签的内容。
<c:set var="score" value="${param.score }"/>
<c:choose>
<c:when test="${score > 100 || score < 0}">错误的分数:${score }</c:when>
<c:when test="${score >= 90 }">A级</c:when>
<c:when test="${score >= 80 }">B级</c:when>
<c:when test="${score >= 70 }">C级</c:when>
<c:when test="${score >= 60 }">D级</c:when>
<c:otherwise>E级</c:otherwise>
</c:choose>
(6)forEach标签:
forEach当前就是循环标签了,forEach标签有多种两种使用方式:
- 使用循环变量,指定开始和结束值,类似for(int i = 1; i <= 10; i++) {};
- 循环遍历集合,类似for(Object o : 集合);
循环变量:
<c:set var="sum" value="0" />
<c:forEach var="i" begin="1" end="10">
<c:set var="sum" value="${sum + i}" />
</c:forEach>
<c:out value="sum = ${sum }"/>
<c:set var="sum" value="0" />
<c:forEach var="i" begin="1" end="10" step ="2">
<c:set var="sum" value="${sum + i}" />
</c:forEach>
<c:out value="sum = ${sum }"/>
遍历集合或数组方式:
<%
String[] names = {"zhangSan", "liSi", "wangWu", "zhaoLiu"};
pageContext.setAttribute("ns", names);
%>
<c:forEach var="item" items="${ns }">
<c:out value="name: ${item }"/><br/>
</c:forEach>
遍历List:
<%
List<String> names = new ArrayList<String>();
names.add("zhangSan");
names.add("liSi");
names.add("wangWu");
names.add("zhaoLiu");
pageContext.setAttribute("ns", names);
%>
<c:forEach var="item" items="${ns }">
<c:out value="name: ${item }"/><br/>
</c:forEach>
遍历Map:
<%
Map<String,String> stu = new LinkedHashMap<String,String>();
stu.put("number", "N_1001");
stu.put("name", "zhangSan");
stu.put("age", "23");
stu.put("sex", "male");
pageContext.setAttribute("stu", stu);
%>
<c:forEach var="item" items="${stu }">
<c:out value="${item.key }: ${item.value }"/><br/>
</c:forEach>
forEach标签还有一个属性:varStatus,这个属性用来指定接收“循环状态”的变量名,例如:<forEach varStatus=”vs” …/>,这时就可以使用vs这个变量来获取循环的状态了。
count:int类型,当前以遍历元素的个数;
index:int类型,当前元素的下标;
first:boolean类型,是否为第一个元素;
last:boolean类型,是否为最后一个元素;
current:Object类型,表示当前项目。
<c:forEach var="item" items="${ns }" varStatus="vs">
<c:if test="${vs.first }">第一行:</c:if>
<c:if test="${vs.last }">最后一行:</c:if>
<c:out value="第${vs.count }行: "/>
<c:out value="[${vs.index }]: "/>
<c:out value="name: ${vs.current }"/><br/>
</c:forEach>
5、fmt标签库常用标签:
fmt标签库是用来格式化输出的,通常需要格式化的有时间和数字。
格式化时间:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
Date date = new Date();
pageContext.setAttribute("d", date);
%>
<fmt:formatDate value="${d }" pattern="yyyy-MM-dd HH:mm:ss"/>
格式化数字:
<%
double d1 = 3.5;
double d2 = 4.4;
pageContext.setAttribute("d1", d1);
pageContext.setAttribute("d2", d2);
%>
<fmt:formatNumber value="${d1 }" pattern="0.00"/><br/>
<fmt:formatNumber value="${d2 }" pattern="#.##"/>
十一.EL表达式介绍
EL表达式定义规则:以 $ 开头 内容写在 {} 中,都是针对Attrbute()的 例: ${test},.
1.EL表达式查找顺序
如果使用类似于 ${username} 的时候没有在哪一个范围查找username 那么它会以:
- Page
- Request
- Session
- Application
为顺序来进行查找,加入中途找到了 username 那么就会返回值 如果一路没找到 返回 null
2.EL表达式的隐性变量
从这里开始就是介绍EL表达式的隐性变量,通过这些隐性变量可以让我们简便完成很多复杂的操作:
pageContext | 用来访问JSP的隐性对象 |
pageScope | page对象的MAP |
requestScope | request对象的MAP |
requestScope | request对象的MAP |
sessionScope | session对象的MAP |
applicationScope | application对象的MAP |
param | 包含请求参数字符串的MAP |
paramValues | 包含请求参数字符串数组的MAP |
header | 包含请求头字符串的MAP |
headerValues | 包含请求头字符串数组的MAP |
cookie | 按名称存储请求附带的cookie的MAP |
例:假设我们是想要session中的name值 那么我们可以使用:
${sessionScope.name}
再例:我们想要获得GET或则POST传递过来的name参数,在以前我们只能使用:
request.getParameter(name);
使用EL表达式可代替为:${param.name}
同样,如果我们想要得到一个参数的数组,可以使用:${paramValues.name}
当然,返回的是一个数组对象.
3.EL常用操作
获得对象的属性
${name}
//如果明确知道是在哪个域,也可以复杂地这样写:
${pageContext.findAttribute("name") }
${requestScope.name}
//<!-- 从WEB域中找到键值为person的对象,然后再person对象中找到name属性 -->
${person.name}
${person.address.city}
//<!-- 也可以用[]方式 -->,例如,健名中有“-”的,就要用这个取值了
${person['name']}
${person['address']['city']}
从List集合对象中获取某个值并显示。
<%
List<Person> list = new ArrayList<Person>();
list.add(new Person("kkk"));
list.add(new Person("sss"));
list.add(new Person("jjj"));
application.setAttribute("list_1", list);
%>
${list_1[1].name }
从Map中获取某个值并显示
<%
Map map = new HashMap();
map.put("a", new Person("aaa"));
map.put("b", new Person("bbb"));
map.put("1", new Person("ccc"));
request.setAttribute("map", map);
%>
${map['1'].name }<!-- 是数字的话只能用括号,就算put进去的key值是字符串类型-->
${map.a.name }
EL运算符
1)语法:${运算表达式}
2)常见运算符:
==(eq) !=(ne) <(lt) >(gt) <=(le) >=(ge) &&(and) ||(or) !(not)
3)判断是否为空:
${empty name }
4)三目运算符:
${name == null?"null":name }
6、获取常用对象
十二.表单提交使用的编码
关于编码的 form 标签的属性
accept-charset:可以指定form编码形式
enctype: 规定在发送表单数据之前如何对其进行编码。
有三种设置类型
默认为application/x-www-form-urlencoded:发送前编码所有字符
multipart/form-data:不对字符编码,包含文件上传控件的表单时,必须使用该值
text/plain:空格转换为 "+" 加号,但不对特殊字符编码。
method:规定用于发送 form-data 的 HTTP 方法。有 post 和 get
默认的,无论表单提交的method是post 还是 get,都会默认使用页面的编码进行数据的编码,但是一旦指定了accept-charset="utf-8"这个参数,那么他就会运用你约定的编码方式来编码你的数据。
页面的编码也就是:<%@ page contentType="text/html;charset=utf-8" pageEncoding="UTF-8"%> 中contentType
<%@ page contentType="text/html;charset=gb2312" language="java"%>
<html>
<head>
<title>title</title>
</head>
<body>
<form method="get" action="${pageContext.request.contextPath}/hello" accept-charset="UTF-8">
name: <input type="text" name="name"/><br/>
age:<input type="text" name="age"/><br/>
<input type="submit" value="提交" />
</form>
如果没有指定 accept-charset="utf-8",则使用页面的编码方式 gb2312,如果指定了则使用 accept-charset="utf-8"指定的 utf-8进行编码
十三.JSP 调用和运行原理(详细版)
问题:
<%@ page contentType="text/html;charset=utf-8" pageEncoding="UTF-8"%> 中 charset="utf-8" 与 pageEncoding="utf-8"区别
关于JSP页面中的pageEncoding和contentType两种属性的区别:
pageEncoding是jsp文件本身的编码(从 jsp到 servlet 源码使用的编码)
contentType的charset是指服务器发送给客户端时的内容编码(从 tomcat 服务器发送到浏览器使用的编码)
pageEncoding 和contentType的预设都是 ISO8859-1. 而随便设定了其中一个, 另一个就跟着一样了. 但这不是绝对的.
JSP要经过两次的“编码”,第一阶段会用pageEncoding,第二阶段会用utf-8至utf-8,第三阶段就是由Tomcat出来的网页, 用的是contentType。
第一阶段是jsp编译成java
它会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码(即.java),如果pageEncoding设定错了,或没有设定,出来的就是中文乱码。
第二阶段是由JAVAC的JAVA源码至java byteCode的编译
不论JSP编写时候用的是什么编码方案,经过这个阶段的结果全部是UTF-8的encoding的java源码。
JAVAC用UTF-8的encoding读取java源码,编译成UTF-8 encoding的二进制码(即.class),这是JVM对常数字串在二进制码(java encoding)内表达的规范。
第三阶段是Tomcat(或其的application container)载入和执行阶段二的来的JAVA二进制码