老杜 JavaWeb 讲解(十七) ——JSP补充(EL+JSTL)
(十六)JSP补充
相关视频:
指令
指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)
指令包括哪些呢?
- include指令:包含指令,在JSP中完成静态包含,很少用了。(这里不讲)
- taglib指令:引入标签库的指令。这个到 JSTL标签库 的时候再学习。现在先不管。
- page指令:目前重点学习一个page指令。
指令的使用语法是什么?
- <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值....%>
关于page指令当中都有哪些常用的属性呢?
<%@page session="true|false" %>
true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。
如果没有设置,默认值就是session="true"
session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。
<%@page contentType="text/json" %>
<%@page contentType="text/html" %>
contentType属性用来设置响应的内容类型
<%@page contentType="text/json;charset=UTF-8" %>
同时也可以设置字符集。
<%@page pageEncoding="UTF-8" %>
pageEncoding="UTF-8" 表示设置响应时采用的字符集。
<%@page import="java.util.List, java.util.Date, java.util.ArrayList" %>
<%@page import="java.util.*" %>
import语句,导包。
<%@page errorPage="/error.jsp" %>
当前页面出现异常之后,跳转到error.jsp页面。
errorPage属性用来指定出错之后的跳转位置。
<%@page isErrorPage="true" %>
在错误页面可以启用JSP九大内置对象之一:exception(刚刚发生的异常对象。)
默认值是false。
exception.printStackTrace();//在控制台打印错误信息
九大内置对象
-
jakarta.servlet.jsp.PageContext pageContext 页面作用域
-
jakarta.servlet.http.HttpServletRequest request 请求作用域
-
jakarta.servlet.http.HttpSession session 会话作用域
-
jakarta.servlet.ServletContext application 应用作用域
- pageContext < request < session < application
- 以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法。
- 以上作用域的使用原则:尽可能使用小的域。
-
java.lang.Throwable exception
-
jakarta.servlet.ServletConfig config
-
java.lang.Object page (其实是this,当前的servlet对象)
-
jakarta.servlet.jsp.JspWriter out (负责输出)
-
jakarta.servlet.http.HttpServletResponse response (负责响应)
- PageContext(页面作用域):它提供了对JSP页面中所有作用域的访问,包括请求(request)、会话(session)和应用(application)作用域。
Object attribute = pageContext.getAttribute("attributeName");
pageContext.setAttribute("attributeName", attributeValue);
- HttpServletRequest(请求作用域):它封装了客户端发送的HTTP请求的信息,可以用于获取请求参数、请求头等。
String param = request.getParameter("param");
String header = request.getHeader("User-Agent");
- HttpSession(会话作用域):它用于跟踪用户的会话状态,可以存储和获取会话特定的数据。
HttpSession session = request.getSession();
session.setAttribute("username", "Alice");
String username = (String) session.getAttribute("username");
- ServletContext(应用作用域):它代表整个Web应用程序的上下文环境,可用于获取应用程序范围内的初始化参数、共享数据等。
ServletContext application = request.getServletContext();
String initParam = application.getInitParameter("initParam");
application.setAttribute("attributeName", attributeValue);
- Throwable(异常对象):通常在异常处理中使用,用于捕获和处理异常。
try {
// 代码块
} catch (Throwable exception) {
// 异常处理逻辑
}
- ServletConfig(Servlet 配置):代表单个Servlet的配置信息,可以获取该Servlet的初始化参数。
String initParam = config.getInitParameter("initParam");
-
Object(页面对象):在JSP页面中,
page
实际上是指向当前生成的 Servlet 类的实例对象。 -
JspWriter(输出流对象):用于在JSP页面中进行输出操作,将内容发送给客户端。
out.println("Hello, JSP!");
- HttpServletResponse(HTTP 响应):用于生成HTTP响应,包括设置状态码、设置响应头、向客户端发送响应内容等。
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>Hello, Servlet!</h1>");
EL表达式
EL表达式是干什么用的?
- Expression Language(表达式语言)
- EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观。
- JSP中夹杂着各种java代码,例如<% java代码 %>、<%=%>等,导致JSP文件很混乱,不好看,不好维护。所以才有了后期的EL表达式。
- EL表达式可以算是JSP语法的一部分。EL表达式归属于JSP。
EL表达式出现在JSP中主要是:
-
从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器。这就是EL表达式的功效。
三大功效:
- 第一功效:从某个域中取数据。
- 四个域:
- pageContext
- request
- session
- application
- 四个域:
- 第二功效:将取出的数据转成字符串。
- 如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。
- 第三功效:将字符串输出到浏览器。
- 和这个一样:<%= %>,将其输出到浏览器。
- 第一功效:从某个域中取数据。
EL表达式很好用,基本的语法格式:
${表达式}
EL表达式的使用:
<%
// 创建User对象
User user = new User();
user.setUsername("jackson");
user.setPassword("1234");
user.setAge(50);
// 将User对象存储到某个域当中。一定要存,因为EL表达式只能从某个范围中取数据。
// 数据是必须存储到四大范围之一的。
request.setAttribute("userObj", user);
%>
<%--使用EL表达式取--%>
${这个位置写什么????这里写的一定是存储到域对象当中时的name}
要这样写:
${userObj}
等同于java代码:<%=request.getAttribute("userObj")%>
你不要这样写:${"userObj"}
<%--如果想输出对象的属性值,怎么办?--%>
${userObj.username} 使用这个语法的前提是:User对象有getUsername()方法。
${userObj.password} 使用这个语法的前提是:User对象有getPassword()方法。
${userObj.age} 使用这个语法的前提是:User对象有getAge()方法。
${userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
${userObj} 底层是怎么做的?
从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器。
EL表达式中的 . 这个语法,实际上调用了底层的getXxx()方法。
注意:如果没有对应的get方法,则出现异常。报500错误。
${userObj.addr222.zipcode}
以上EL表达式对应的java代码:
user.getAddr222().getZipcode()
面试题:${abc} 和 ${"abc"}的区别是什么?
${abc}表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码: 域.setAttribute("abc", 对象);
${"abc"} 表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。
注意点:
-
EL表达式优先从小范围中读取数据。
范围大小关系:pageContext < request < session < application
-
EL表达式中有四个隐含的隐式的范围:
pageScope 对应的是 pageContext范围。
requestScope 对应的是 request范围。
sessionScope 对应的是 session范围。
applicationScope 对应的是 application范围。
-
EL表达式对null进行了预处理。如果是null,则向浏览器输出一个空字符串。
-
EL表达式取数据的时候有两种形式:
-
第一种:. (大部分使用这种方式)
-
第二种:[ ] (如果存储到域的时候,这个name中含有特殊字符,可以使用 [ ])
- request.setAttribute("abc.def", "zhangsan");
- ${requestScope.abc.def} 这样是无法取值的。
- 应该这样:$
-
-
掌握使用EL表达式,怎么从Map集合中取数据:
${map.key}
- 掌握使用EL表达式,怎么从数组和List集合中取数据:
${数组[0]}
${数组[1]}
${list[0]}
-
page指令当中,有一个属性,可以忽略EL表达式
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %> isELIgnored="true" 表示忽略EL表达式 isELIgnored="false" 表示不忽略EL表达式。(这是默认值) isELIgnored="true" 这个是全局的控制。 可以使用反斜杠进行局部控制: \${username} 这样也可以忽略某个EL表达式。
-
通过EL表达式获取应用的根:
${pageContext.request.contextPath}
-
EL表达式中其他的隐式对象:
pageContext
param
paramValues
initParam
没有request对象,如果想要获取,需要借助pageContext对象的getRequest()方法。
pageContext
(页面上下文):在JSP(JavaServer Pages)中,pageContext
是一个对象,提供了访问当前JSP页面环境的方法。它可以用来获取请求和响应对象、存取和共享属性、操作会话等。param
(参数):在Servlet中,param
通常指HTTP请求中的参数信息。当客户端向服务器发送HTTP请求时,可以使用参数来传递数据。在Servlet中,可以使用request.getParameter(name)
方法来获取特定名称的参数值。paramValues
(参数值):对于具有相同名称的多个参数,paramValues
是一个数组,包含了这些参数的所有值。在Servlet中,可以使用request.getParameterValues(name)
方法获取具有相同名称的参数值数组。initParam
(初始参数):在Servlet的部署描述符(web.xml)中,可以定义一些初始化参数(init-param)。这些参数可以在Servlet初始化时读取,以配置其行为。在Servlet中,可以使用getInitParameter(name)
方法来获取特定名称的初始化参数值。
以下是将常规代码和EL表达式写在一起的示例:
- 获取
pageContext
对象:
常规代码:
String contextPath = request.getContextPath();
EL表达式:
${pageContext.request.contextPath}
- 获取单个请求参数
param
的值:
常规代码:
String paramValue = request.getParameter("param");
EL表达式:
${param.paramName}
- 获取多个请求参数
param
的值(如果存在多个同名参数):
常规代码:
String[] paramValues = request.getParameterValues("param");
EL表达式:
${paramValues.paramName[0]} ${paramValues.paramName[1]}
- 获取初始化参数
initParam
的值:
常规代码:
<web-app> ... <context-param> <param-name>initParamName</param-name> <param-value>initParamValue</param-value> </context-param> ... </web-app>
// 获取ServletContext对象 ServletContext servletContext = getServletContext(); // 通过参数名称获取参数值 String initParamValue = servletContext.getInitParameter("initParamName");
EL表达式:
${initParam.initParamName}
通过使用EL表达式,我们可以简化常规代码中获取数据的过程。EL表达式提供了一种更简洁、易读的方式来访问和操作页面上下文、请求参数和初始化参数的值。将EL表达式直接嵌入到JSP页面中,可以使代码更清晰、简洁,并提高开发效率。
-
EL表达式的运算符
-
算术运算符:
${5 + 3} // 输出结果:8 ${5 + "3"} // 输出结果:8 ${10 - 2} // 输出结果:8 ${4 * 2} // 输出结果:8 ${16 / 2} // 输出结果:8 ${17 % 3} // 输出结果:2
EL表达式中的+号不会做字符串拼接,永远求和。
加号两边不是数字的时候,一定会将它们转换为数字,如果转换不了就会报错:NumberFormatException(数字格式异常)
-
关系运算符:
${5 == 5} // 输出结果:true ${10 != 5} // 输出结果:true ${8 > 5} // 输出结果:true ${3 < 2} // 输出结果:false ${4 >= 4} // 输出结果:true ${7 <= 4} // 输出结果:false ${5 eq 5} // 输出结果:true ${10 eq 5} // 输出结果:false ${'Hello' eq 'Hello'} // 输出结果:true ${user.name eq 'John Doe'} // 假设user对象有一个name属性,如果name属性的值为"John Doe",则输出结果为true
<%@ page contentType="text/html;charset=UTF-8"%> <% String s1 = new String("hehe"); String s2 = new String("hehe"); request.setAttribute("s1",s1); request.setAttribute("s2",s2); %> ${s1 == s2}<br> <%--true--%> <% Object o = new Object(); request.setAttribute("o0",o); request.setAttribute("o00",o); %> ${o0 == o00}<br> <%--true--%> <% Object o1 = new Object(); Object o2 = new Object(); request.setAttribute("o1",o1); request.setAttribute("o2",o2); %> ${o1 == o2}<br> <%--false--%>
==号,在EL表达式中调用的是equals()方法。
!=号,在EL表达式中调用的是equals()方法。
-
逻辑运算符:
${true && false} // 输出结果:false ${true || false} // 输出结果:true ${!true} // 输出结果:false
-
empty运算符:
empty运算符 empty运算符的结果是boolean类型 ${empty param.username} ${not empty param.username} ${!empty param.password}
-
空安全运算符:
${user?.username} / 如果user对象不为null,则输出用户的名称;否则,输出空串 ${empty param.username}
-
字符串连接运算符:
${'Hello' + 'World'} // 输出结果:HelloWorld ${firstName + ' ' + lastName} // 假设firstName为"John",lastName为"Doe",输出结果:John Doe
-
集合运算符:
${array[0]} // 假设array是一个整型数组,输出数组的第一个元素 ${list.size()} // 假设list是一个集合,输出集合的大小 ${person.name.length()} // 假设person对象有一个name属性,输出name属性值的长度
-
JSTL标签库
什么是JSTL标签库?
- Java Standard Tag Lib(Java标准的标签库)
- JSTL标签库通常结合EL表达式一起使用。目的是让JSP中的java代码消失。
- 标签是写在JSP当中的,但实际上最终还是要执行对应的java程序。(java程序在jar包当中。)
使用JSTL标签库的步骤:
-
第一步:引入JSTL标签库对应的jar包。
- tomcat10之后引入的jar包是:
- jakarta.servlet.jsp.jstl-2.0.0.jar
- jakarta.servlet.jsp.jstl-api-2.0.0.jar
- 在IDEA当中怎么引入?
- 在WEB-INF下新建lib目录,然后将jar包拷贝到lib当中。然后将其“Add Lib...”
- 一定是要和mysql的数据库驱动一样,都是放在WEB-INF/lib目录下的。
- 什么时候需要将jar包放到WEB-INF/lib目录下?如果这个jar是tomcat服务器没有的。
- tomcat10之后引入的jar包是:
-
第二步:在JSP中引入要使用标签库。(使用taglib指令引入标签库。)
-
JSTL提供了很多种标签,你要引入哪个标签????重点掌握核心标签库。
-
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 这个就是核心标签库。 prefix="这里随便起一个名字就行了,核心标签库,大家默认的叫做c。"
-
-
第三步:在需要使用标签的位置使用即可。表面使用的是标签,底层实际上还是java程序。
JSTL标签的原理
-
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 以上uri后面的路径实际上指向了一个xxx.tld文件。 tld文件实际上是一个xml配置文件。 在tld文件中描述了“标签”和“java类”之间的关系。 以上核心标签库对应的tld文件是:c.tld文件。它在哪里? 在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件。
-
源码解析:配置文件tld解析
<tag> <description>对该标签的描述</description> <name>catch</name> 标签的名字 <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class> 标签对应的java类。 <body-content>JSP</body-content> 标签体当中可以出现的内容,如果是JSP,就表示标签体中可以出现符合JSP所有语法的代码。例如EL表达式。 <attribute> <description>对这个属性的描述</description> <name>var</name> 属性名 <required>false</required> false表示该属性不是必须的。true表示该属性是必须的。 <rtexprvalue>false</rtexprvalue> 这个描述说明了该属性是否支持EL表达式。false不支持。true支持EL表达式。 </attribute> </tag>
<c:catch var=""> JSP.... </c:forEach>
jstl中的核心标签库core当中有哪些常用的标签呢?
-
c:if
<c:if test="${empty param.username}"> <h1>用户名不能为空。</h1> </c:if> <c:if test="${not empty param.username}"> <h1>欢迎你。</h1> ${param.username} <%=request.getParameter("username")%> </c:if>
-
c:forEach
<c:forEach var="i" begin="1" end="10" step="2"> ${i}<br> </c:forEach>
<c:forEach items="集合,支持EL表达式" var="集合中的元素" varStatus="元素状态对象"> ${元素状态对象.count} </c:forEach>
<!-- 使用c:forEach标签遍历stuList列表 --> <!-- var指定循环变量s,varStatus指定循环状态stuStatus(用来计数)--> <c:forEach items="${stuList}" var="s" varStatus="stuStatus"> 编号:${stuStatus.count} ,age: ${s.age},name: ${s.name}<br> </c:forEach>
-
c:choose when otherwise
<c:choose> <c:when test=""></c:when> <c:when test=""></c:when> <c:when test=""></c:when> <c:otherwise></c:otherwise> </c:choose> <%--相当于: if(){ }else if(){ }else if(){ }else if(){ }else{ } --%>
<c:choose> <c:when test="${param.age < 18}"> 青少年 </c:when> <c:when test="${param.age < 35}"> 青年 </c:when> <c:when test="${param.age < 55}"> 中年 </c:when> <c:otherwise> 老年 </c:otherwise> </c:choose>