0%

JSP深入

  • JSP指令、JSP九大内置对象、EL表达式

6.JSP深入

6.1 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。
      
      1
      2
      3
      4
      5
      6
      7
      8

      - ```jsp
      <%@page contentType="text/json" %>
      contentType属性用来设置响应的内容类型
      但同时也可以设置字符集。
      默认 text/html
      设置字符集为UTF-8:
      <%@page contentType="text/json;charset=UTF-8" %>
    • <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %>
      <%@page import="java.util.*" %>
      import语句,导包。
      
      1
      2
      3
      4
      5

      - ```jsp
      <%@page errorPage="/error.jsp" %>
      当前页面出现异常之后,跳转到error.jsp页面。
      errorPage属性用来指定出错之后的跳转位置。
    • <%@page isErrorPage="true" %>
      表示启用JSP九大内置对象之一:exception
      默认值是false。
      为true表示当前配置的error页面可以使用异常对象exception,用exception.printStackTrace();来打印错误信息
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

      ```jsp
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%@ page isErrorPage="true" %>
      <html>
      <head>
      <title>error page</title>
      </head>
      <body>
      <h1>发生错误!</h1>
      <%
      exception.printStackTrace();
      %>
      </body>
      </html>

6.2 JSP九大内置对象

  • 四个域对象:

    • jakarta.servlet.jsp.PageContext pageContext (页面作用域)

    • jakarta.servlet.http.HttpServletRequest request (请求作用域,一次请求可以跨多页页面)

    • jakarta.servlet.http.HttpSession session (会话作用域)

    • jakarta.servlet.ServletContext application (应用作用域)

      • pageContext < request < session < application 。
      • 以上四个作用域都有:setAttributegetAttributeremoveAttribute 方法。
      • 以上作用域的使用原则:尽可能使用小的域。
  • jakarta.servlet.ServletConfig config

  • java.lang.Throwable exception (异常对象)

  • java.lang.Object page (其实是 this,指当前的 servlet 对象)

  • 负责输出响应

    • jakarta.servlet.jsp.JspWriter out (负责输出)
    • jakarta.servlet.http.HttpServletResponse response (负责响应)

6.3 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 表达式用于取数据,然后在页面进行数据的展示。

6.3.1 EL表达式语法

  • EL 表达式很好用,基本的语法格式:${表达式}

    • 例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <%@page contentType="text/html;charset=UTF-8" %>

      <%
      // 向request作用域当中存储username为zhangsan
      request.setAttribute("username", "zhangsan");
      request.setAttribute("obj", new Object());
      %>

      <%--将request域当中的数据取出来,并且还要输出到浏览器,使用java代码怎么办?--%>
      <%=request.getAttribute("username")%>
      <br>
      <%=request.getAttribute("obj")%>
      <br>

      <hr>

      <%--使用EL表达式呢?--%>
      ${username}
      <br>
      ${obj}

6.3.2 EL表达式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%
// 创建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"}
  • 注意:如果对四个域中的几个都存了 User 对象,例如:

    • pageContext.setAttribute("userObj", user1)
    • request.setAttribute("userObj", user2)
    • session.setAttribute("userObj", user3)
    • application.setAttribute("userObj", user4)

    在没有指定范围的情况下,EL 表达式优先从小范围中读取数据。即 ${userObj} 会优先取出 pageContext 中的 user1 对象。

    • EL 表达式中有四个隐含的隐式的范围:

      • pageScope 对应的是 pageContext 范围。
      • requestScope 对应的是 request 范围。
      • sessionScope 对应的是 session 范围。
      • applicationScope 对应的是 application 范围。

      如果**要从指定的域中取数据,需要按如下语法取:${范围.数据名}**。

      • 例如 ${requestScope.userObj} 会取出 request 中的 user2 对象。
    • EL 表达式取数据的时候有两种形式:

      • 第一种:. (大部分使用这种方式)

      • 第二种:[ ] (如果存储到域的时候,这个 name 中含有特殊字符,可以使用 [ ])

        • request.setAttribute("abc.def", "zhangsan");
        • ${requestScope.abc.def} 这样是无法取值的。

        应该这样:${requestScope["abc.def"]}

  • EL 表达式对 null 进行了预处理。如果是 null,则向浏览器输出一个空字符串。

    • ${userObj} 这个 EL 表达式等同于这一段 java 代码:<%=request.getAttribute("userObj") == null ? "" : request.getAttribute("userObj")%>
  • ${abc}${"abc"} 的区别是什么?

    • ${abc} 表示从某个域中取出数据,并且被取的这个数据的 name 是 “abc”,之前一定有这样的代码:域.setAttribute("abc", 对象);
    • ${"abc"} 表示直接将 “abc” 当做普通字符串输出到浏览器,不会从某个域中取数据了。

    ${userObj} 底层是怎么做的?

    • 从域中取数据,取出 user 对象;然后调用 user 对象的 toString 方法,转换成字符串;最终输出到浏览器。
  • 如果想输出对象的属性值,怎么办?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <%--如果想输出对象的属性值,怎么办?--%>
    ${userObj.username} 使用这个语法的前提是:User对象有getUsername()方法。
    ${userObj.password} 使用这个语法的前提是:User对象有getPassword()方法。
    ${userObj.age} 使用这个语法的前提是:User对象有getAge()方法。
    ${userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
    没有对应的属性,有对应的get方法不会报错
    EL表达式中的 “.” 这个语法,实际上调用了底层的getXxx()方法。
    注意:如果没有对应的get方法,则出现异常。报500错误。

    ${userObj.addr222.zipcode}
    以上EL表达式对应的java代码:
    user.getAddr222().getZipcode()

    注意:在 EL 表达式中,${user.email} 和 User 对象有没有 email 属性并没有太大关系,只要 User 类中有 getEmail 方法,${user.email} 就会表示为该方法的返回值。另一种写法是 ${user["email"]}

    • 可以简单的理解为,${user.email} 就是 ${user.getEmail()} 的缩写。

6.3.3 从Map集合中取数据

  • ${map.key}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="com.f.javaweb.jsp.bean.User " %>
<%@ page contentType="text/html; charset=UTF-8" %>

<%
// 一个Map集合
Map<String,String> map = new HashMap<>();
map.put("username", "zhangsan");
map.put("password", "123");
// 将Map集合存储到request域当中。
request.setAttribute("userMap", map);

// 另一个Map集合
Map<String,User> map2 = new HashMap<>();
User user = new User();
user.setName("lisi");
map2.put("user", user);
request.setAttribute("userMap2", map2);
%>

<%--使用EL表达式,将map中的数据取出,并输出到浏览器--%>
${userMap.username}
<br>
${userMap.password}
<br>
${userMap["username"]}
<br>
${userMap["password"]}
<hr>

<%--使用EL表达式将map2中的user对象中的username取出--%>
${userMap2.user.name}

6.3.4 从数组和List集合中取数据

  • ${数组[index]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<%@ page import="com.f.javaweb.jsp.bean.User" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.HashSet" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%
// 数组对象
String[] usernames = {"zhangsan", "lisi", "wangwu"};
// 向域中存储数组
request.setAttribute("nameArray", usernames);

User u1 = new User();
u1.setName("zhangsan");
User u2 = new User();
u2.setName("lisi");
User[] users = {u1, u2};
request.setAttribute("userArray", users);

// List对象
List<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
request.setAttribute("myList", list);

// Set对象
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
request.setAttribute("set", set);
%>

<%--使用EL表达式取出数组中的元素--%>
${nameArray} <%--将数组对象直接输出--%>
<br/>
${nameArray[0]} <%--取出数组中的第一个元素--%>
<br/>
${nameArray[1]}
<br/>
${nameArray[2]}
<br/>
<%--取不出数据,在浏览器上直接显示的就是空白。不会出现数组下表越界。--%>
${nameArray[100]}
<hr>

<%--取出数组中第二个用户的用户名--%>
${userArray[1].name}
<hr>

<%--取出List集合--%>
<%--list集合也是通过下标的方式取数据的。--%>
${myList}
<br/>
${myList[0]}
<br/>
${myList[1]}
<hr>

<%--取出set集合--%>
set集合:${set}
<%--无法获取:PropertyNotFoundException: 类型[java.util.HashSet]上找不到属性[0]--%>
<%-- set无序,没有索引 --%>

6.3.5 EL表达式其他细节

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

    1
    2
    3
    4
    5
    6
    7
    <%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
    isELIgnored="true" 表示忽略EL表达式,整个EL表达式会被当成一个字符串输出在浏览器
    isELIgnored="false" 表示不忽略EL表达式。(这是默认值)

    isELIgnored="true" 这个是全局的控制。整个JSP页面的EL表达式

    可以使用反斜杠进行局部控制:\${username} 这样也可以忽略EL表达式。
  • 通过 EL 表达式获取应用的根:${pageContext.request.contextPath}

    • pageContext.getRequest() 可以获取 request 对象。

    • 在 EL 表达式当中没有 request 这个隐式对象,requestScope 这个只代表“请求范围”,不等同于 request 对象。

      在 EL 表达式当中有一个隐式的对象:pageContext。EL 表达式中的 pageContext 和 JSP 中的九大内置对象 pageContext 是同一个对象。

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

    • pageContext
    • param,用于获取只有一个值的请求参数
    • paramValues,用于获取有多个值的请求参数
    • initParam,用于获取 Servlet 上下文请求参数,在 ServletContext
    • 其他…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<%@page contentType="text/html;charset=UTF-8" %>

<%--JSP中EL表达式的隐含对象:
1. pageContext
2. param
3. paramValues
4. initParam
5. 其他(不是重点)
--%>
应用的根路径:${pageContext.request.contextPath}<br>

<%--request是JSP九大内置对象之一。--%>
<%--request.getParameter("username") 获取请求的参数。--%>
<%--用户在浏览器上提交数据:http://localhost:8080/jsp/implicit.jsp?username=lisi --%>
用户名:<%=request.getParameter("username")%><br>
用户名:${param.username}<br>

<%--假设用户提交的数据:http://localhost:8080/jsp/implicit.jsp?aihao=smoke&aihao=drink&aihao=tangtou --%>
<%--以上提交的数据显然是采用checkbox进行提交的。同一组的checkbox的name是一样的。--%>
<%--param 获取的是请求参数一维数组当中的第一个元素。--%>
爱好:<%=request.getParameter("aihao")%> <br>
爱好:${param.aihao} <br>

一维数组:<%=request.getParameterValues("aihao")%><br>
一维数组:${paramValues.aihao}<br>
<%--获取数组当中的元素:[下标]--%>
爱好:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]} <br>

<%--EL表达式中的隐含对象:initParam--%>
<%--ServletContext是Servlet上下文对象,对应的JSP九大内置对象之一是:application --%>
<%
String a = application.getInitParameter("pageSize");
String b = application.getInitParameter("pageNum");
%>
每页显示的记录条数:<%=a%> <br>
页码:<%=b%> <br>

每页显示的记录条数:<%=application.getInitParameter("pageSize")%> <br>
页码:<%=application.getInitParameter("pageNum")%> <br>

每页显示的记录条数:${initParam.pageSize} <br>
页码:${initParam.pageNum} <br>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">

<!--Servlet上下文初始化参数-->
<!--上下文初始化参数被封装到:ServletContext对象当中了。-->
<context-param>
<param-name>pageSize</param-name>
<param-value>20</param-value>
</context-param>

<context-param>
<param-name>pageNum</param-name>
<param-value>5</param-value>
</context-param>

</web-app>

6.3.6 EL表达式的运算符

  • EL 表达式的运算符有以下几种:
    • 算术运算符
      • +、-、*、/、%
    • 关系运算符
      • ==、!=、>、>=、<、<=
    • 逻辑运算符
      • !、&&、||
    • 条件运算符
      • ? :
    • 取值运算符
      • [ ].
    • empty 运算符
      • empty 运算符的结果是 boolean 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<%@ page import="com.f.javaweb.jsp.bean.Student" %>
<%@page contentType="text/html;charset=UTF-8" %>

<%--
关于EL表达式中的运算符
1. 算术运算符
+ - * / %
2. 关系运算符
== != > >= < <= eq
3. 逻辑运算符
! && || not and or (注意:!和not都是取反)
4. 条件运算符
? :
5. 取值运算符:
[] 和 .
6. empty 运算符
--%>
<%-- 算术运算符 --%>
${10 + 20} <br/> <%--30--%>

<%-- 在EL表达式当中“+”运算符只能做求和运算。不会进行字符串拼接操作。 --%>
<%--"20"会被自动转换成数字20--%>
${10 + "20"} <br/> <%--30--%>

<%-- java.lang.NumberFormatException: For input string: "abc" --%>
<%-- + 两边不是数字的时候,一定会转成数字。转不成数字就报错:NumberFormatException--%>
\${10 + "abc"} <br/>
\${"king" + "soft"} <br/>

<hr>

<%-- 关系运算符 --%>
${"abc" == "abc"} <br/> <%--true--%>
${"abc"} == ${"abc"} <br/> <%--直接被打印输出:abc == abc--%>

<%
Object obj = new Object();
request.setAttribute("k1", obj);
request.setAttribute("k2", obj);
%>
${k1 == k2} <br/> <%--true--%>

<%
String s1 = new String("hehe");
String s2 = new String("hehe");
request.setAttribute("a", s1);
request.setAttribute("b", s2);
%>
因为字符串String重写了equals方法 <br/>
${a == b} <br/> <%--true--%>

<%
Object o1 = new Object();
Object o2 = new Object();
request.setAttribute("o1", o1);
request.setAttribute("o2", o2);
%>
${o1 == o2} <br/> <%--false--%>

<%
Student stu1 = new Student("小明", 10);
Student stu2 = new Student("小明", 10);

request.setAttribute("stu1", stu1);
request.setAttribute("stu2", stu2);
%>

<%--EL表达式当中的 "==" 调用了被比较对象的equals方法。--%>
${stu1 == stu2} <br/> <%--true--%>
<%-- == 和 eq 都会调用被比较对象的equals方法--%>
${stu1 eq stu2} <br/> <%--true--%>

<%-- != 也会调用被比较对象的equals方法。--%>
${stu1 != stu2} <br/> <%--false--%>
${200 != 200} <br/> <%--false--%>

<%--以下语法错误,没有加小括号--%>
\${!stu1 eq stu2} <br/>
<%--正确的--%>
${!(stu1 eq stu2)}<br/> <%--false--%>
${not(stu1 eq stu2)}<br/> <%--false--%>

<hr>

<%--条件运算符--%>
${empty param.username ? "对不起,用户名不能为空!" : "欢迎访问!!!!" } <br/>

<%--empty运算符:运算结果是boolean类型。--%>
<%--判断是否为空,如果为空,结果是true。如果不为空,结果是false--%>
${empty param.username} <br/>
${!empty param.username} <br/>
${not empty param.username} <br/>

<%--结果false--%>
<%--前半部分是boolean类型。后面是null--%>
${empty param.password == null} <br/>
${(empty param.password) == null} <br/>

${param.password == null} <br/> <%--true--%>
---------------The End---------------