EL 表达式
最初出现的目的是为了取代 JSP 页面中的 "脚本表达式<%= %>",格式:${...}
。但随着 EL 的发展,其功能已不限于此。
获取数据
- 获取常量:字符串、数字、布尔类型
${"root" } ${12345 } ${true }
- 获取域中的变量
- 如果表达式中写的是一个变量名,则 EL 会调用 pageContext 的
findAttriubte()
,在 4 大作用域中以给定的名字找对应的属性值,然后进行输出 - 如果四个域中都找不到,则什么都不输出
- 如果表达式中写的是一个变量名,则 EL 会调用 pageContext 的
- 获取数组中的数据
<% String[] names = {"A","B","C","D"}; pageContext.setAttribute("names", names); %> ${names[0] }
- 获取集合中的数据
- 获取 Collection 中的数据
<% List<String> list = new ArrayList<String>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); pageContext.setAttribute("list", list); %> ${list[2] }
- 获取 Map 中的数据
-
map["key"]
和map.key
在使用上的不同:当 key 是数字或者包含特殊字符-/.
,只能用map["key"]
- 注意如下这二者的差别
-
- 获取 Collection 中的数据
- 获取 JavaBean 的属性
-
${obj.xxx}
这句话的底层其实是 EL 调用了 obj 的getXxx()
- 在写路径的时候,同理可得:
${pageContext.request.contextPath}
-
执行运算
- 算术运算
-
+
|-
|*
|/
- 如果有非数字参与算术运算,EL 表达式会试图将非数字转换为数字后参与运算
${1+‘1‘} → 2 ${1+‘a‘} → 抛异常
-
- 比较运算
< lt > gt <= le >= ge != ne == eq -------- ${1 eq 1}、${3 ge 2}
- 逻辑运算
&& and || or ! not -------- ${not (3>2) and 1<2 or 10>3} => false
- 三元运算符:
${7>6 ? "yes" : "no"}
- empty 运算符:判断一个对象是否为 null / 字符串是否为空字符串 / 集合内容是否为空 / 域中是否没有任何属性
获取常用开发对象
EL 中内置了 11 个对象,这些对象不需要提前定义就可以直接使用
- pageContext:可以很方便的获取 JSP 页面中的 9 大隐式对象;如 ${pageContext.request|session|...}
- pageScope:page域中属性组成的 Map
- requestScope:request域中属性组成的 Map
- sessionScope:session域中属性组成的 Map
- applicationScope:application域中属性组成的 Map
- param:所有请求参数组成的 Map<String,String>
- paramValues:所有请求参数组成的 Map<String,String[]>;如 ${paramValues.hobbies[2]}
- header:所有请求头组成的 Map<String,String>
- headerValues:所有请求头组成的 Map<String,String[]>
- cookie:所有 Cookie 信息组成的 Map<String,Cookie>;如 ${cookie.JSESSIONID.value}
- initParam:web 应用的所有初始化参数组成的 Map<String,String>;如 ${initParam.name1}
《web.xml》 <context-param> <param-name>name1</param-name> <param-value>value1</param-value> </context-param>
调用方法
不是任意方法都可以被调用,需要满足如下 3 个条件:
- 静态方法
- 写一个 tld 文件,在其中对要被调用的静态方法进行描述
- 用 taglib 指令将 tld 文件引入当前 JSP 页面,从而在 JSP 页面中调用描述好的方法
举例:对给定的 URL 进行 URL 编码
ELFunc.java
public class ELFunc {
public static String encode(String str, String encode) {
try {
return URLEncoder.encode(str, encode);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
MyELFunc.tld(放在 WEB-INF 目录下)
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" 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">
<tlib-version>1.0</tlib-version>
<short-name>MyELFunc</short-name>
<uri>http://www.nuist.edu.cn/MyELFunc</uri>
<function>
<name>urlEncode</name>
<function-class>cn.edu.nuist.util.ELFunc</function-class>
<function-signature>
java.lang.String encode(java.lang.String, java.lang.String)
</function-signature>
</function>
</taglib>
el3.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.nuist.edu.cn/MyELFunc" prefix="MyELFunc" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>使用EL表达式调用方法</title>
</head>
<body>
调用自定义函数:${MyELFunc:urlEncode("徐州","utf-8") }
调用JSTL函数库中的函数:${fn:toLowerCase("JAFIOjifeoFIAWfjiof") }
</body>
</html>
JSTL 中提供了一套 EL 自定义函数,这些函数包含了 JSP 页面制作者经常要用到的字符串操作。例如, fn:toLowerCase
将字符串中的字符变为小写,fn:indexOf
返回一个指定字符串在另一个字符串中第一次出现的索引位置。JSTL 中提供的 EL 自定义函数必须在 EL 表达式中使用。
- fn:toLowerCase
- fn:toUpperCase
- fn:trim
- fn:escapeXml
- fn:length
- fn:split
- fn:join
- fn:indexOf
- fn:contains
- fn:containsIgnoreCase
- fn:startsWith
- fn:startsWith
- fn:replace
- fn:substring
- fn:substringAfter
- fn:substringBefore
JSTL 标签库
全称:JavaServer Pages Standard Tag Library。提供给 Java Web 开发人员一个标准通用的标签函数库和 EL 配合来取代传统直接在页面上嵌入 Java 程序(Scripting)的做法,以提高程序可读性、维护性和方便性。
导入 [核心标签库c]:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
-
<c:out>
:标签用于输出一段文本内容到 pageContext 对象当前保存的 out 对象中- 输出常量:
<c:out value="<hhhh>"></c:out>
- 输出变量:
<c:out value="${name }"></c:out>
- 输出默认值:
<c:out value="${addr }" default="XZ"></c:out>
- 输出转义字符:
<c:out value="<a href=‘#‘>xxx</a>"></c:out>
- 输出常量:
-
<c:set>
- 设置/修改域中的属性值 (默认 page 域)
<c:set scope="request" var="name" value="金敏喜"></c:set>
- 设置/修改域中 Map 的值
<% Map<String, String> map = new HashMap<String, String>(); pageContext.setAttribute("map", map); %> <h4>target属性的值是Map对象,property属性表示该Map对象的某个key</h4> <c:set target="${map }" property="cellphone" value="10086"></c:set> ${map.cellphone }
- 设置/修改域中 JavaBean 的值
<% Person p = new Person(); pageContext.setAttribute("p", p); %> <h4>target属性的值是JavaBean对象,property属性表示JavaBean对象的属性</h4> <c:set target="${p }" property="name" value="玉珠"></c:set> ${p.name }
- 设置/修改域中的属性值 (默认 page 域)
-
<c:remove>
- 删除域中属性(如果不写 scope 属性,会把 4 个域中叫 var 的属性都删除)
- 格式:
<c:remove var="varName" [scope="{page|request|session|application}"] />
-
<c:catch>
- 用于捕获嵌套在标签体中的内容抛出的异常,var 属性用于标识 <c:catch> 标签捕获的异常对象,它将保存在 page 域中
- 示例
<c:catch var="myex" > <% 10/0; %> </c:catch> 异常:<c:out value="${myex}" /> ${myex}<br /> 异常 myex.getMessage:<c:out value="${myex.message}" /><br /> 异常 myex.getCause:<c:out value="${myex.cause}" /><br /> 异常 myex.getStackTrace:<c:out value="${myex.stackTrace}" />
-
<c:if>
- 用来构造简单的“if-then”结构的条件表达式
- 例如:
<c:if test="${6<7 }">you will.</c:if>
-
<c:choose>
-
<c:choose>
标签用于指定多个条件选择的组合边界,它必须与<c:when>
和<c:otherwise>
标签一起使用。使用<c:choose>
,<c:when>
和<c:otherwise>
三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。 - 示例
<% int day = 1; pageContext.setAttribute("day", day); %> <c:choose> <c:when test="${day == 1 }">周一</c:when> <c:when test="${day == 2 }">周二</c:when> <c:when test="${day == 3 }">周三</c:when> <c:when test="${day == 4 }">周四</c:when> <c:when test="${day == 5 }">周五</c:when> <c:otherwise>周末</c:otherwise> </c:choose>
-
-
<c:forEach>
<h1>遍历数组中的数据</h1> <% String[] cities = {"北京", "上海", "广州", "深圳"}; pageContext.setAttribute("cities", cities); %> <c:forEach items="${cities }" var="c"> ${c } </c:forEach> <hr/> <h1>遍历集合中的数据</h1> <% List<String> list = new ArrayList<String>(); list.add("英国"); list.add("法国"); list.add("德国"); list.add("荷兰"); pageContext.setAttribute("list", list); %> <c:forEach items="${list }" var="country"> ${country } </c:forEach> <hr/> <h1>遍历Map中的数据</h1> <% Map<String,String> map = new HashMap<String, String>(); map.put("aaa", "111"); map.put("bbb", "222"); map.put("ccc", "333"); map.put("ddd", "444"); pageContext.setAttribute("map", map); %> <c:forEach items="${map }" var="entry"> ${entry.key } —— ${entry.value } </c:forEach> <hr/> <h1>循环执行指定的内容若干次</h1> <c:forEach begin="0" end="10" step="2" var="i"> ${i } </c:forEach> <hr/> <h1>varStatus 属性:遍历10~100的偶数, 如果"数字所在的位置"是3的倍数, 显示成红色</h1> <c:forEach begin="10" end="100" step="2" var="i" varStatus="stat"> <c:if test="${stat.count % 3 == 0 }"> <font color="red">${i }</font> </c:if> <c:if test="${stat.count % 3 != 0 }"> <font color="blue">${i }</font> </c:if> </c:forEach>
-
<c:forTokens>
<c:forTokens items="www.nuist.edu.cn" delims="." var="str"> ${str } <br/> </c:forTokens>
-
<c:import>
:请求包含
-
<c:redirect>
:请求重定向
-
<c:url>
- 用于在 JSP 页面中构造一个 URL 地址,其主要目的是实现 URL 重写(URL 重写就是将会话标识号以参数形式附加在URL地址后面)
- 示例
<c:url value="/index.jsp" context="${pageContext.request.contextPath }" var="url" scope="page"></c:url> <a href="${url }"></a>
-
<c:param>
:配合上述三个标签使用;示例:<c:param name="name" value="value" />
自定义标签
传统标签
生命周期
第一次被使用时创建出来, 后续一直驻留在内存中, 为后续请求服务
开发步骤
以显示客户机 IP 为例
- 写一个类实现
javax.servlet.jsp.tagext.Tag<I>
public class ShowIPTag implements Tag{ private PageContext pc; public void setPageContext(PageContext pc) { // 1. this.pc = pc; } public void setParent(Tag t) { // 2. 传入的是当前标签的"父标签", 没有则传null } public Tag getParent() { return null; } public int doStartTag() throws JspException { // 3. try { pc.getOut().write(pc.getRequest().getRemoteAddr()); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } return 0; } public int doEndTag() throws JspException { // 4. return 0; } public void release() {} }
- 创建一个 tld 文件,放在 WEB-INF 目录下,描述写好的类
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" 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"> <tlib-version>1.0</tlib-version> <short-name>MyTag</short-name> <uri>http://www.nuist.edu.cn/MyTag</uri> <tag> <name>showip</name> <tag-class>cn.edu.nuist.tag.ShowIPTag</tag-class> <body-content>empty</body-content> </tag> </taglib>
- JSP:任意内容 (简单标签不支持)
- Scriptless:任意 JSP 内容,不包括 Java 代码
- empty:该标签是个自闭标签
- tagdependent:标签体是给后台用的 (没人用)
- 在 JSP 页面中
- 引入 tld 文件:
<%@ taglib uri="http://www.nuist.edu.cn/MyTag" prefix="MyTag" %>
- 在 JSP 页面中使用"自定义标签":
<MyTag:showip/>
- 引入 tld 文件:
简单标签
开发步骤
- 写一个类实现
SimpleTag<I>
/ 继承它的默认实现类SimpleTagSupport
- 写一个 tld 文件,描述写好的类
- 在 JSP 页面中,引入 tld 文件,就可以在 JSP 页面中使用"自定义标签"了
执行过程
当 JSP 在执行的过程中,每当遇到一个简单标签时,都会创建一个处理类对象(如下步骤是实现接口方式的;若是继承默认实现类方式,则父类会把前几步替你做了,你就只需要提供属性声明、属性对应 setXxx()
和 doTag()
即可)
- 传入当前 JSP 页面的 PageContext 对象
setJspContext(JspContext pc)
- 如果当前标签有父标签,则调用
setParent(JspTag parent)
将父标签传入;如果没有父标签,则这个方法不会被调用 - 如果该标签具有属性,调用属性的
setXxx()
将属性的值传入 - 如果当前标签具有标签体,则会调用
setJspBody(JspFragment jspBody)
将封装了标签体信息的 JspFragment 传入;如果没有标签体,则这个方法不会被调用 - 最后,调用
doTag()
,在这个方法里我们可以书写处理【标签事件】的 Java 代码 - 当自定义标签执行完成后,自定义简单标签对象就销毁掉了
- 为自定义标签增加属性
- 在标签处理类中增加一个 JavaBean 属性,这个属性就是要增加的标签的属性,并对外提供
setXxx()
- 在 tld 文件中这个标签的描述中描述一下该属性
<attribute> <name>标签的属性名必须与对应的标签类的成员变量名一致</name> <required>是否是必须的属性</required> <rtexprvalue>是否支持EL表达式</rtexprvalue> <type>属性的数据类型</type> </attribute>
案例
- 实例1:显示客户机 IP 功能(实现接口方式)
public class SimpleShowIPTag implements SimpleTag { private JspContext pc; public void doTag() throws JspException, IOException { PageContext pcx = (PageContext) pc; pcx.getOut().write(pcx.getRequest().getRemoteAddr()); } public void setParent(JspTag parent) {} public JspTag getParent() { return null; } public void setJspContext(JspContext pc) { this.pc = pc; } public void setJspBody(JspFragment jspBody) {} }
- 实例2:没啥目的,纯粹为了展示标签类里的功能(继承类方式)
public class DemoSimpleTag extends SimpleTagSupport { // 成员属性对应自定义标签的属性 private int times; // 对外必须提供 setXxx() public void setTimes(int times) { this.times = times; } @Override public void doTag() throws JspException, IOException { // 功能1: 控制标签体是否执行 // 不执行 —— 什么都不做,标签体就不会执行 // 执行 —— 调用封装着标签体的 JspFragment 对象的 invoke() // 参数可以传 null, 如果传 null 底层还是会打给 out 缓冲区 // getJspBody().invoke(getJspContext().getOut()); // 功能2: 控制标签之后的内容是否执行 // 执行 —— 什么都不做 // 不执行 —— 抛出SkipPageException // throw new SkipPageException(); // 功能3: 控制标签体重复执行 for(int i = 0; i < times; i++) getJspBody().invoke(null); // 功能4: 修改标签体后进行输出 /* StringWriter writer = new StringWriter(); // 目的地为String 的一个字符输出流 JspFragment jspBody = getJspBody(); jspBody.invoke(writer); String str = writer.toString(); str = str.toUpperCase(); getJspContext().getOut().write(str); */ } }
- 对应到 MyTag.tld
<tag> <name>simpleshowip</name> <tag-class>cn.edu.nuist.simpletag.SimpleShowIPTag</tag-class> <body-content>empty</body-content> </tag> <tag> <name>demosimpletag</name> <tag-class>cn.edu.nuist.simpletag.DemoSimpleTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>times</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>int</type> </attribute> </tag>
案例
If 标签
public class IfTag extends SimpleTagSupport {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
@Override
public void doTag() throws JspException, IOException {
if(test) getJspBody().invoke(null);
}
}
<tag>
<name>if</name>
<tag-class>cn.edu.nuist.simpletag.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
</tag>
防盗链标签
public class RefTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
PageContext pC = (PageContext) getJspContext();
HttpServletRequest request = (HttpServletRequest) pC.getRequest();
HttpServletResponse response = (HttpServletResponse) pC.getResponse();
String ref= request.getHeader("referer");
if(ref == null || "".equals(ref) || !ref.startsWith("http://localhost")) {
response.sendRedirect(request.getContextPath() + "/index.jsp");
return;
}
}
}
<tag>
<name>ref</name>
<tag-class>cn.edu.nuist.simpletag.RefTag</tag-class>
<body-content>empty</body-content>
</tag>
转义标签
public class TranHTMLTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
StringWriter writer = new StringWriter();
getJspBody().invoke(writer);
String str = writer.toString();
// 转义
str = filter(str);
getJspContext().getOut().write(str);
}
private String filter(String message) {
if (message == null) return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case ‘<‘:
result.append("<");
break;
case ‘>‘:
result.append(">");
break;
case ‘&‘:
result.append("&");
break;
case ‘"‘:
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
<tag>
<name>tran</name>
<tag-class>cn.edu.nuist.simpletag.TranHTMLTag</tag-class>
<body-content>scriptless</body-content>
</tag>
分页查询功能
功能分析
- 在 index.jsp 提供客户信息列表页面的超链接,超链接尾部记得加上 "?thisPage=1"
- PageCustServlet
- 获取要访问的页码以及每页显示多少条记录
- 调用service中分页查询客户的方法
- 将查到的结果存入 request 域带到 pageList.jsp 做显示
- 提供一个JavaBean —— Page,属性如下:
private int thisPage; private int rowPerPage; private int countRow; private int countPage; private int firstPage; private int lastPage; private int prePage; private int nextPage; private List<Cust> list;
- 效果:共 xxx 条记录 共 xx 页 首页 上一页 1 2 3 4 5 下一页 尾页
分页技术
逻辑分页
- 原理
- 在 SQL 查询时,先从 DB 检索出所有数据的结果集
- 在程序内,通过逻辑语句获取分页所需要的数据
- 如:检索 11-20 条
userList.subList(10,20);
- 缺点
- 如果有 10000 条记录呢,也全部放在内存中吗
- 如果数据有变动呢,集合中也反应不出来
- 适用情景: 在基本不会变动、记录数少的情况下使用
物理分页
- 原理
- 在SQL查询时,从DB只检索分页需要的数据
- 通常不同的DB有着不同的物理分页语句
- MySQL:采用
limit
关键字- 如:检索 11-20 条
SELECT * FROM user LIMIT 10,10;
- param1:索引(从0开始);param2:查取多少条
- 如:检索 11-20 条
代码实现
- CustDaoImpl
public List<Cust> getCustByScope(int from, int count) { String sql = "select * from customer limit ?,?"; QueryRunner runner = new QueryRunner(DaoUtils.getSource()); try { return runner.query(sql, new BeanListHandler<Cust>(Cust.class),from,count); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } }
- CustServiceImpl
public Page pageCust(int thisPage, int rowPerPage) { Page page = new Page(); page.setThisPage(thisPage); page.setRowPerPage(rowPerPage); int countRow = dao.getCountRow(); page.setCountRow(countRow); int countPage = countRow%rowPerPage==0 ? countRow/rowPerPage : countRow/rowPerPage+1; page.setCountPage(countPage); page.setFirstPage(1); page.setLastPage(countPage); page.setPrePage(thisPage==1 ? 1 : thisPage-1); page.setNextPage(thisPage==countPage ? countPage : thisPage+1); int index = (thisPage-1) * rowPerPage; page.setList(dao.getCustByScope(index, rowPerPage)); return page; }
- PageCustServlet
public class PageCustServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { CustService service = BasicFactory.getFactory().getInstance(CustService.class); // 获取当前要显示的页 int thisPage = Integer.parseInt(request.getParameter("thisPage")); // 每页记录数就设成10 int rowPerPage = 10; // 调用 service 中分页查询客户的方法 Page page = service.pageCust(thisPage,rowPerPage); // 存入 request 域中,带到 pageCust.jsp 做显示 request.setAttribute("page", page); request.getRequestDispatcher("/pageCust.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- pageList.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>分页查询</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <script type="text/javascript"> function changePage(obj) { if(isNaN(obj.value)) { alert("页码必须是数字!"); obj.value = "${page.thisPage}"; return; } else if(obj.value <= 0 || obj.value > ${page.countPage}) { alert("页码必须在有效范围内!"); obj.value="${page.thisPage}"; return; } else { window.location.href = "${pageContext.request.contextPath}/servlet/PageCustServlet?thisPage=" + obj.value; } } </script> </head> <body> <div align="center"> <h1>分页查询</h1> <table border="1"> <tr> <th>客户姓名</th> <th>客户性别</th> <th>出生日期</th> <th>手机号码</th> <th>电子邮箱</th> <th>客户爱好</th> <th>客户类型</th> <th>描述信息</th> </tr> <c:forEach items="${requestScope.page.list }" var="cust"> <tr> <td><c:out value="${cust.name }"/></td> <td><c:out value="${cust.gender }"/></td> <td><c:out value="${cust.birthday }"/></td> <td><c:out value="${cust.cellphone }"/></td> <td><c:out value="${cust.email }"/></td> <td><c:out value="${cust.preference }"/></td> <td><c:out value="${cust.type }"/></td> <td><c:out value="${cust.description }"/></td> </tr> </c:forEach> </table> 共${page.countRow }条记录 共${page.countPage }页 <a href="${pageContext.request.contextPath }/servlet/PageCustServlet?thisPage=${page.firstPage}">首页</a> <a href="${pageContext.request.contextPath }/servlet/PageCustServlet?thisPage=${page.prePage}">上一页</a> <!-- 分页逻辑开始 --> <c:if test="${page.countPage <= 5 }"> <c:set var="begin" value="1" scope="page"/> <c:set var="end" value="${page.countPage }" scope="page"/> </c:if> <c:if test="${page.countPage > 5 }"> <c:choose> <c:when test="${page.thisPage <= 3 }"> <c:set var="begin" value="1" scope="page"/> <c:set var="end" value="5" scope="page"/> </c:when> <c:when test="${page.thisPage >= page.countPage - 2 }"> <c:set var="begin" value="${page.countPage - 4 }" scope="page"/> <c:set var="end" value="${page.countPage }" scope="page"/> </c:when> <c:otherwise> <c:set var="begin" value="${page.thisPage - 2 }" scope="page"/> <c:set var="end" value="${page.thisPage + 2 }" scope="page"/> </c:otherwise> </c:choose> </c:if> <c:forEach begin="${begin }" end="${end }" var="i"> <c:if test="${page.thisPage != i }"> <a href="${pageContext.request.contextPath }/servlet/PageCustServlet?thisPage=${i}">${i }</a> </c:if> <c:if test="${page.thisPage == i }">${i }</c:if> </c:forEach> <!-- 分页逻辑结束 --> <a href="${pageContext.request.contextPath }/servlet/PageCustServlet?thisPage=${page.nextPage}">下一页</a> <a href="${pageContext.request.contextPath }/servlet/PageCustServlet?thisPage=${page.lastPage}">尾页</a> 跳到 <input type="text" value="${page.thisPage }" style="width: 50px;" onchange="changePage(this)"> 页 </div> </body> </html>