1 EL表达式
EL表达式(Express Language,表达式语言),凡是称之为表达式的技术,都一般用来输出数据,不能进行复杂的业务逻辑。类似前一天JSP中的“<%= %>”,都是只能取值不能赋值。注意,不管EL表达式放在JSP文件的什么位置,加载时都会被Java虚拟机先编译,在写的位置只是展示作用。
EL表达式是一个可以跨技术应用的语法,不同于JSP的表达式“<%= %>”的是,它在JS、JSP中都可以使用,一定程度上实现了Java和JS的值共享。下面的1.1章节是实现值共享的方式。
EL表达式的格式:
${要输出的数据}
1.1 JSP九种内置对象在EL表达式中的应用
EL表达式应用了JSP内置对象后,在Java、JS中均可被使用,实现值共享。
JSP内置对象 | EL表达式版 | EL表达式范围中提取值 |
---|---|---|
pageContext | ${pageContext} | ${pageScope.key} |
request | ${pageContext.request} | ${requestScope.key} |
session | ${pageContext.session} | ${sessionScope.key} |
application | ${pageContext.servletContext} | ${applicationScope.key} |
response | ${pageContext.response} | |
config | ${pageContext.servletConfig} | |
out | ${pageContext.out} | |
page | ${pageContext.page} | |
exception | ${pageContext.exception} |
1.2 特性测试
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>JspDay2_el_jstl</display-name>
<!--
配置一个全局参数
-->
<context-param>
<param-name>context_param_key</param-name>
<param-value>context_param_value</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
index.jsp
<%@ page language="java" import="java.util.*,com.test.po.Hero" pageEncoding="UTF-8" isErrorPage="true" %>
<!DOCTYPE HTML>
<html>
<head>
<title>EL表达式(Express Language)</title>
</head>
<body>
<h3>算术运算</h3>
${"3+1"}=${3+1} <br />
${"10-5"}=${10-5} <br />
${"2*3"}=${2*3} <br />
<h3>关系运算</h3>
${3>1} <br />
${2<10} <br />
<h3>逻辑运算</h3>
${3>1&&2<5} <br />
${1<0||3>10} <br />
<h3>三目运算</h3>
${3>1?true:false} <br />
${2<10?1:0} <br />
<h3>范围取值</h3>
<%
pageContext.setAttribute("key","valuePage");
request.setAttribute("key","valueRequest");
session.setAttribute("key","valueSession");
application.setAttribute("key","valueApplication");
%>
<%--
JSP对应的源码中pageContext、config、application、session、request、response已定义好,
可以直接拿来用(见前一天的源码截图)
--%>
<%=pageContext.getAttribute("key") %>
<%=request.getAttribute("key") %>
<%=session.getAttribute("key") %>
<%=application.getAttribute("key") %>
<br />
<%--
上面的JSP代码在EL表达式中如下书写:
${范围.key} 注意点后面的是一个key值,对应setAttribute(对应这里,Object);
范围有如下四种写法:
pageScope requestScope sessionScope applicationScope
注意在没有重复的key的前提下,范围一般省略不写
如果不书写范围,存在多个重复的key,则默认输出现存范围最小的
源码中是这样转换的,其余同理:
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${pageScope.key}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
out.write("\r\n");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${key}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
out.write("\r\n");
--%>
${pageScope.key}
${requestScope.key}
${sessionScope.key}
${applicationScope.key}
${key}
<br />
<h3>拿取配置在web.xml中的全局参数</h3>
<%=application.getInitParameter("context_param_key") %>
<br />
${initParam.context_param_key}
<h3>接受客户端通过表单或者链接提交过来的值</h3>
接受值:<%=request.getParameter("test_param") %>
<br />
<%--
${param.发送过来的key},上面的Java代码若没有值会显示null
下面的EL表达式若没有值什么都不显示
--%>
接受值:${param.test_param}
<h3>拿取自定义数据类型的属性值</h3>
<%
//创建一个自定义数据类型
Hero hero = new Hero(1,"盖伦","战士");
//赋范围
session.setAttribute("myhero",hero);
%>
<a href="show.jsp">查看效果</a>
</body>
</html>
效果图
表单或链接传值
index1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title></title>
</head>
<body>
<% String str = "eric"; %>
<a href="index.jsp?test_param=<%=str %>">使用链接传递值</a>
<form action="index.jsp" method="get">
传值: <input type="text" name="test_param"
required />
<br />
<input type="submit" value="提交" />
</form>
</body>
</html>
使用表单传值时get后面的参数可以自定义,两个接受值也显示同一个传过来的参数
点击index.jsp中的链接后跳转到的show.jsp
<%@ page language="java"
import="java.util.*,com.test.po.Hero" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title></title>
</head>
<body>
拿取实体类属性:<%=((Hero)session.getAttribute("myhero")).getName() %>
<br />
<%--
${范围.key.属性名}
--%>
拿取实体类属性:${sessionScope.myhero.name}
<h3>空验证</h3>
<%--
如果empty后面的范围取值不为空,则返回false,如果取值为空,则返回true
--%>
${empty pageScope.judgeEmpty}
<h3>注意</h3>
<%--
注意EL表达式并不是什么都可以往外拿取,必须根据范围提供的key往外拿取
值,只能取存入Scope中的数据。
如果没有定义范围且此时只有一个key为这个名字,可以直接取到这个key对应的value;
没有定义范围且多个key重名,此时默认取最小范围的那个值。
若key本身不存在,根本无法取值,也不输出null等内容,什么反应都没有
--%>
<% int i = 3;%>
取值1:<%=i %>
取值2:${i}
<br />
<%
i = 4;
pageContext.setAttribute("i",i);
%>
取值3:${pageScope.i}
取值4:${i}
<%--
若要在EL表达式中嵌套EL表达式,则被嵌套的EL表达式不需要写大括号。
--%>
</body>
</html>
效果图:
2 JSTL标签(c标签)
JSTL(JSP Standard Tag Lib,JSP标准标签库),因为引入的taglib指令标签中prefix为c(core,核心),后面的标签中冒号前也为c,所以也叫c标签。
JSP页面不能大量存在Java代码 ,若这样会让页面混乱不堪且浏览器从上到下加载很慢。所以页面内前一天写的JSP页面内写的Java代码需要转换为JSTL标签展示。
规范地编写JSP页面,是要把页面元素全部都被包裹在标签内,所以c标签会存在很多嵌套JSP标签和EL表达式的情况。
<%@ page language="java" import="java.util.*,com.test.po.Hero" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML>
<html>
<head>
<title>JSTL</title>
</head>
<body>
<%--
1)赋值
var:表示key值
value:表示value值
scope:表示范围
这里支持四种范围 page(PageContext) request(HttpServletRequest)
session(HttpSession) application(ServletContext)
--%>
<c:set var="testVar" value="testPage" scope="page"></c:set>
<c:set var="testVar" value="testRequest" scope="request"></c:set>
<c:set var="testVar" value="testSession" scope="session"></c:set>
<c:set var="testVar" value="testApplication" scope="application"></c:set>
<%--
2)取值
可直接书写Java代码
直接输出
通过EL表达式拿取(推荐)
--%>
<c:out value="我是直接输出字符串!!!"></c:out>
<% String str = "我是赋值的变量"; %>
<c:out value="<%=str %>"></c:out>
<c:out value="${pageScope.testVar}"></c:out>
<c:out value="${requestScope.testVar}"></c:out>
<c:out value="${sessionScope.testVar}"></c:out>
<c:out value="${applicationScope.testVar}"></c:out>
<%--
3)删除值
var:要删除的键值对的键
scope:要删除的键值对的范围
注意如果不指定范围,则默认符合键的全部删除
--%>
<c:remove var="testVar" scope="request" />
<%--
default:如果value取值失败,则输出default值
--%>
<c:out value="${requestScope.testVar}" default="删除成功!"></c:out>
<%
List<Hero> list = new ArrayList<Hero>();
list.add(new Hero(1,"盖伦","战士"));
list.add(new Hero(2,"孙悟空","战士"));
list.add(new Hero(3,"宫本武藏","战士"));
list.add(new Hero(4,"露娜","战士"));
session.setAttribute("mylist",list);
%>
<%--
4)重定向
--%>
<c:redirect url="showtb.jsp"></c:redirect>
</body>
</html>
在注释重定向标签时,效果图是:
重定向后的showtb.jsp
<%@ page language="java"
import="java.util.*,com.test.po.Hero" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML">
<html>
<head>
<title></title>
<style>
table{
width:500px;
border-collapse:collapse;
}
table,tr,td{
border:solid 2px silver;
}
</style>
</head>
<body>
<table>
<tr>
<td>ID</td>
<td>英雄名</td>
<td>英雄类型</td>
</tr>
<%--
5)循环遍历
items:循环体,可以从Java代码或者El表达式中拿取
var:表示每次遍历时循环体中被遍历对象的key值,
范围默认是pageContext范围
begin:起始索引值
end:结束索引值
注意索引值从0开始
step:步长,以几条数据为一个单位进行遍历,默认是1
这里从第三条选取到了第五条,因为没有第五条所以只输出到最后即结束
--%>
<c:forEach items="${sessionScope.mylist}"
var="mList" begin="2" end="4">
<tr>
<td><c:out value="${pageScope.mList.id}"></c:out></td>
<td><c:out value="${mList.name}"></c:out></td>
<td><c:out value="${mList.type}"></c:out></td>
</tr>
</c:forEach>
</table>
<%--
6)流程控制
test如果为true则进入内部分支
--%>
<c:if test="${3>1}">
<c:out value="输出这里啦!!!"></c:out>
</c:if>
<%--
6)流程控制
<c:choose>中的<c:when>、<c:otherwise>互为互斥条件
类似于 java 中的 if else.
--%>
<c:choose>
<c:when test="${3>10}">
<c:out value="输出这里"></c:out>
</c:when>
<c:otherwise>
<c:out value="最终输出这里啦!!"></c:out>
</c:otherwise>
</c:choose>
</body>
</html>
效果图:
3 JSP的分页
分页有两种形式:假分页,一次将所有的数据全部取出,用户需要哪几条就显示哪几条,用于数据不多但是有强大的缓存机制情况,现在用得少;真分页,用户需要哪几条就从数据库中取出哪几条,不会预先一次性全部取出。
3.1 分页公式
select 字段 from 表 limit x, y;
x:起始索引值
y:本页一共显示多少条记录
3.2 分页四要素及公式演化
不管使用何种技术,完成分页需要获取这四个参数,其中:
要素名称 | 获取方式 |
---|---|
总记录数 | 通过dao到数据库中查询 |
每页记录数 | 自定义 |
总页数* | (总记录数 + 每页记录数 - 1)/每页记录数 |
当前页 | 默认值是1,此值会随着用户的操作不断发生变化 |
-
*总页数公式如何记:
-
以上前三个值都是常量,最后一个值为变量。
-
公式演化:
select 字段 from 表 limit x, y x:(当前页-1)*每页记录数 [起始索引值] y:[本页一共显示多少条记录]
以16条记录,每页5条,分4页为例:
页名 | 页码范围 | 公式计算x,y | x,y |
---|---|---|---|
page1 | 1~5 | (1-1)*5,5 | 0,5 |
page2 | 1~5 | (2-1)*5,5 | 5,5 |
page3 | 1~5 | (3-1)*5,5 | 10,5 |
page4 | 1~5 | (4-1)*5,5 | 15,5 |
3.3 编码测试
省略实体类、dao层的步骤,着重测试分页功能。
<%@ page language="java"
import="java.util.*,com.test.po.Computer,com.test.dao.*"
pageEncoding="UTF-8"
contentType="text/html; charset=utf-8"%>
<% request.setCharacterEncoding("utf-8"); %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML>
<html>
<head>
<title>JSP分页</title>
<style>
table,tr,th,td{
border:solid 2px silver;
}
table{
width:500px;
border-collapse:collapse;
}
th{
background-color:gray;
}
</style>
</head>
<body>
<%
ComputerDaoIf dao = new ComputerDaoImpl();
//1:拿取总记录数
Integer allRecord = dao.getCount();
//2:拿取每页记录数
Integer pageRecord = 5;
//3:拿取总页数
Integer allPage = (allRecord+pageRecord-1)/pageRecord;
//4:设置当前页
Integer currentPage = 1;
//接受表单提交的值
String value = request.getParameter("cup");
if(value!=null){
currentPage = Integer.parseInt(value);
}
List<Computer> list = dao.getPage((currentPage-1)*pageRecord,pageRecord);
request.setAttribute("mylist",list);
%>
<table>
<tr>
<th>ID</th>
<th>品名</th>
<th>售价</th>
<th>产地</th>
</tr>
<c:forEach items="${mylist}" var="test">
<tr>
<td><c:out value="${test.id}"></c:out></td>
<td><c:out value="${test.name}"></c:out></td>
<td><c:out value="${test.total}"></c:out></td>
<td><c:out value="${test.location}"></c:out></td>
</tr>
</c:forEach>
</table>
<form action="index.jsp" method="get">
<%--隐藏表单域,当前在第几页--%>
<input type="hidden" name="cup" />
<%--当已经是首页时,首页按钮被禁用--%>
<input type="button" value="首页" onclick="change(1)" <%=currentPage==1?"disabled":"" %> />
<%--当已经是首页时,上一页按钮被禁用--%>
<input type="button" value="上一页" onclick="change(<%=currentPage-1 %>)" <%=currentPage==1?"disabled":"" %> />
<%--当已经是末页时,下一页按钮被禁用--%>
<input type="button" value="下一页" onclick="change(<%=currentPage+1 %>)" <%=currentPage==allPage?"disabled":"" %> />
<%--当已经是末页时,下一页按钮被禁用--%>
<input type="button" value="末页" onclick="change(<%=allPage %>)" <%=currentPage==allPage?"disabled":"" %> />
</form>
<script>
function change(cr){
//将当前页的值赋值给隐藏域的value值
document.forms[0].cup.value = cr;
//使用JS提交表单
document.forms[0].submit();
}
</script>
</body>
</html>
效果图:
4 Servlet、JSP和HTML的中文编码的问题
当出现乱码问题时,首先检查代码的拼写,若没有问题检查添加如下代码。
4.1 Servlet
4.1.1 doPost
-
使用软编码
response.setContentType("text/html;charset=编码"); request.setCharacterEncoding("编码");
4.1.2 doGet
-
使用软编码
response.setContentType("text/html;charset=编码"); request.setCharacterEncoding("编码");
-
若软编码无效
在Tomcat的conf文件夹中修改server.xml的第69行代码 添加如下语句,使Tomcat中的项目不按浏览器的地址栏中编码方式进行编码, 而是按照页面内的编码方式编码 useBodyEncodingForURI="true"
-
若以上方法均无效,只能将获取到的Get值使用硬编码按字节强转:
public void doGet(HttpServletRequest request,HttpServletResposne) throws Exception{ response.setContentType("text/html;charset=编码"); request.setCharacterEncoding("编码"); String name = request.getParameter("key"); String newName = change(name); } //硬编码 public String change(String old){ try{ return new String(old.getBytes("iso-8859-1"),"编码"); }catch(Exception ex){ ex.printStackTrace(); return null; } }
4.2 JSP
<%@ page contentType="text/html;charset=utf-8" %>
<% request.setCharacterEncoding("utf-8"); %>
4.3 HTML
head标签中添加<meta charset=“utf-8”/>