老杜 JavaWeb 讲解(十七) ——JSP补充

发布时间 2023-08-02 18:39:11作者: 猪无名

(十六)JSP补充

相关视频:

49-JSP的page指令以及九大内置对象和EL表达式

指令

指令的作用:指导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 (负责响应)

  1. PageContext(页面作用域):它提供了对JSP页面中所有作用域的访问,包括请求(request)、会话(session)和应用(application)作用域。
Object attribute = pageContext.getAttribute("attributeName");
pageContext.setAttribute("attributeName", attributeValue);
  1. HttpServletRequest(请求作用域):它封装了客户端发送的HTTP请求的信息,可以用于获取请求参数、请求头等。
String param = request.getParameter("param");
String header = request.getHeader("User-Agent");
  1. HttpSession(会话作用域):它用于跟踪用户的会话状态,可以存储和获取会话特定的数据。
HttpSession session = request.getSession();
session.setAttribute("username", "Alice");
String username = (String) session.getAttribute("username");
  1. ServletContext(应用作用域):它代表整个Web应用程序的上下文环境,可用于获取应用程序范围内的初始化参数、共享数据等。
ServletContext application = request.getServletContext();
String initParam = application.getInitParameter("initParam");
application.setAttribute("attributeName", attributeValue);
  1. Throwable(异常对象):通常在异常处理中使用,用于捕获和处理异常。
try {
    // 代码块
} catch (Throwable exception) {
    // 异常处理逻辑
}
  1. ServletConfig(Servlet 配置):代表单个Servlet的配置信息,可以获取该Servlet的初始化参数。
String initParam = config.getInitParameter("initParam");
  1. Object(页面对象):在JSP页面中,page 实际上是指向当前生成的 Servlet 类的实例对象。

  2. JspWriter(输出流对象):用于在JSP页面中进行输出操作,将内容发送给客户端。

out.println("Hello, JSP!");
  1. 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集合中取数据:

    • $
  • 掌握使用EL表达式,怎么从数组和List集合中取数据:

    • $
    • $
    • $
  • page指令当中,有一个属性,可以忽略EL表达式

    <%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
    isELIgnored="true"      表示忽略EL表达式
    isELIgnored="false"     表示不忽略EL表达式。(这是默认值)
    
    isELIgnored="true"      这个是全局的控制。
    
    可以使用反斜杠进行局部控制:
    \${username} 
    这样也可以忽略某个EL表达式。
    
  • 通过EL表达式获取应用的根:

    • $
  • EL表达式中其他的隐式对象:

    pageContext

    param

    paramValues

    initParam

    没有request对象,如果想要获取,需要借助pageContext对象的getRequest()方法。

    1. pageContext(页面上下文):在JSP(JavaServer Pages)中,pageContext是一个对象,提供了访问当前JSP页面环境的方法。它可以用来获取请求和响应对象、存取和共享属性、操作会话等。
    2. param(参数):在Servlet中,param通常指HTTP请求中的参数信息。当客户端向服务器发送HTTP请求时,可以使用参数来传递数据。在Servlet中,可以使用request.getParameter(name)方法来获取特定名称的参数值。
    3. paramValues(参数值):对于具有相同名称的多个参数,paramValues是一个数组,包含了这些参数的所有值。在Servlet中,可以使用request.getParameterValues(name)方法获取具有相同名称的参数值数组。
    4. initParam(初始参数):在Servlet的部署描述符(web.xml)中,可以定义一些初始化参数(init-param)。这些参数可以在Servlet初始化时读取,以配置其行为。在Servlet中,可以使用getInitParameter(name)方法来获取特定名称的初始化参数值。

    以下是将常规代码和EL表达式写在一起的示例:

    1. 获取pageContext对象:

    常规代码:

    String contextPath = request.getContextPath();
    

    EL表达式:

    ${pageContext.request.contextPath}
    
    1. 获取单个请求参数param的值:

    常规代码:

    String paramValue = request.getParameter("param");
    

    EL表达式:

    ${param.paramName}
    
    1. 获取多个请求参数param的值(如果存在多个同名参数):

    常规代码:

    String[] paramValues = request.getParameterValues("param");
    

    EL表达式:

    ${paramValues.paramName[0]}
    ${paramValues.paramName[1]}
    
    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表达式的运算符

    1. 算术运算符:

      ${5 + 3}  // 输出结果:8
      ${5 + "3"}  // 输出结果:8   
      ${10 - 2}  // 输出结果:8
      ${4 * 2}  // 输出结果:8
      ${16 / 2}  // 输出结果:8
      ${17 % 3}  // 输出结果:2
      

      EL表达式中的+号不会做字符串拼接,永远求和。

      加号两边不是数字的时候,一定会将它们转换为数字,如果转换不了就会报错:NumberFormatException(数字格式异常)

    2. 关系运算符:

      ${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()方法。

    3. 逻辑运算符:

      ${true && false}  // 输出结果:false
      ${true || false}  // 输出结果:true
      ${!true}  // 输出结果:false
      
    4. empty运算符:

      empty运算符
      
      empty运算符的结果是boolean类型
      
      ${empty param.username}
      ${not empty param.username}
      ${!empty param.password}
      
    5. 空安全运算符:

      ${user?.username}     / 如果user对象不为null,则输出用户的名称;否则,输出空串
      ${empty param.username}
      
    6. 字符串连接运算符:

      ${'Hello' + 'World'}  // 输出结果:HelloWorld
      ${firstName + ' ' + lastName}  // 假设firstName为"John",lastName为"Doe",输出结果:John Doe
      
    7. 集合运算符:

      ${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服务器没有的。
  • 第二步:在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>