JSP
今日目标:
理解 JSP 及 JSP 原理
能在 JSP中使用
EL表达式
和JSTL标签
1,JSP 概述
JSP(全称:Java Server Pages):Java 服务端页面 。是一种动态的网页技术,其中既可以定义 HTML、JS、CSS等静态内容,还可以定义 Java代码的动态内容,也就是
JSP = HTML + Java
。如下就是jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello JSP</title>
</head>
<body>
<h1>你好 JSP</h1>
<%
System.out.println("你好 Java代码");
%>
</body>
</html>
在浏览器地址栏输入:http://localhost:8080/jsp/01_Hello_JSP.jsp
显示结果为:
思考:JSP 能做什么呢?
JSP 作用:简化开发,避免了在Servlet中直接输出HTML标签。
2,JSP 快速入门
接下来我们做一个简单的快速入门代码。
2.1 搭建环境
创建一个maven的 web 项目,项目结构如下:
pom.xml
文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zbbmeta</groupId>
<artifactId>07_JSP</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2.2 导入 JSP 依赖
在 dependencies
标签中导入 JSP 的依赖,如下
<!-- jsp-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<!--因为tomcat存在jsp的jar包,我们只是在开发阶段使用,索引scope选择为provided-->
<scope>provided</scope>
</dependency>
2.3 创建 jsp 页面
在项目的 webapp
下创建jsp页面
通过上面方式创建一个名为 hello.jsp
的页面。
2.4 编写代码
在 hello.jsp
页面中书写 HTML
标签和 Java
代码,如下
<%--
Created by IntelliJ IDEA.
User: zbb
Date: 0029
Time: 0:32
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello JSP</title>
</head>
<body>
<h1>你好 JSP</h1>
<%
System.out.println("你好 Java代码");
%>
</body>
</html>
2.5 测试
启动服务器并在浏览器地址栏输入 http://localhost:8080/jsp/01-hello-jsp.jsp
,我们可以在页面上看到如下内容
在 idea
的控制台看到输出的 你好 Java代码
内容。
3,JSP 原理
思考:为什么还可以写 Java
代码呢?
因为JSP 本质上就是一个 Servlet
-
浏览器第一次访问
01-hello-jsp.jsp
页面 -
tomcat
会将01-hello-jsp.jsp
转换为名为_01_002dhello_002djsp_jsp.java
的一个Servlet
-
tomcat
再将转换的servlet
编译成字节码文件_01_002dhello_002djsp_jsp.class
-
tomcat
会执行该字节码文件,向外提供服务
打开 _01_002dhello_002djsp_jsp.java
文件,来查看里面的代码
public final class _01_002dhello_002djsp_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
可以看出其继承 HttpJspBase
这个类,那我们在看该类的继承关系。到资料中的找如下目录: apache-tomcat-9.0.65-src\java\org\apache\jasper\runtime
(可自行到Tomcat官网下载src压缩包) ,该目录下就有 HttpJspBase
类,查看该类的继承关系
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {
可以看到该类继承了 HttpServlet
;那么 _01_002dhello_002djsp_jsp
这个类就间接的继承了 HttpServlet
,也就说明 _01_002dhello_002djsp_jsp
是一个 servlet
。
在HttpJspBase
类中我们发现了Servlet类中的一个重要方法service(HttpServletRequest request, HttpServletResponse response)
public final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
_jspService(request, response);
}
@Override
public abstract void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
发现service方法调用了一个抽象方法_jspService 思考:java基础中抽象方法由谁实现?
抽象方法由其子类去实现
所以查看 _01_002dhello_002djsp_jsp
类的代码,可以看到有一个名为 _jspService()
的方法,该方法就是每次访问 jsp
时自动执行的方法。
而在 _jspService()
方法中可以看到往浏览器写标签的代码:
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Hello JSP</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <h1>你好 JSP</h1>\r\n");
out.write(" ");
System.out.println("你好 Java代码");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
以前我们自己写 servlet
时,现有由Tomcat自动生成这部分代码,这也就是JSP的作用:简化Servlet的开发
4,JSP 脚本
JSP脚本用于在 JSP页面内定义 Java代码
4.1 JSP 脚本分类
JSP 脚本有如下三个分类:
-
<%...%>:内容会直接放到_jspService()方法之中
-
<%=...%>:内容会放到out.print()中,作为out.print()的参数
-
<%!...%>:内容会放到_jspService()方法之外,被类直接包含 我们主要学习前两种分类。 代码演示:
创建02script.jsp
,并书写内容
<%
String name = "张三";
//注意:最好泛型不要省略
List<String> listStrs = new ArrayList<String>();
listStrs.add("王五");
listStrs.add("老六");
listStrs.add("赵四");
%>
通过浏览器访问 02script.jsp
后,查看转换的 _02script_jsp.java
文件,name和listStrs 变量定义在了 _jspService()
方法中
String name = "张三";
//注意:最好泛型不要省略
List<String> listStrs = new ArrayList<String>();
listStrs.add("王五");
listStrs.add("老六");
listStrs.add("赵四");
在 02script.jsp
中书写
<%=name%>
<%=listStrs%>
通过浏览器访问 02script.jsp
后,查看转换的 _02script_jsp.java
文件,该脚本的内容被放在了 out.print()
中,作为参数
out.print(name);
out.write("\r\n");
out.write(" ");
out.print(listStrs);
浏览器输出结果:
在 hello.jsp
中书写
<%!
String address="河南";
void test(){
System.out.println(address);
}
%>
通过浏览器访问 02script.jsp
后,查看转换的 _02script_jsp.java
文件,该脚本的内容被放在了成员位置
4.2 JSP 缺点
通过上面的案例,我们可以看到 JSP 的很多缺点。
由于 JSP页面内,既可以定义 HTML 标签,又可以定义 Java代码,造成了以下问题:
-
书写麻烦:特别是复杂的页面
既要写 HTML 标签,还要写 Java 代码
-
阅读麻烦
-
复杂度高:运行需要依赖于各种环境,JRE,JSP容器,JavaEE...
-
占内存和磁盘:JSP会自动生成.java和.class文件占磁盘,运行的是.class文件占内存
-
调试困难:出错后,需要找到自动生成的.java文件进行调试
-
不利于团队协作:前端人员不会 Java,后端人员不精 HTML
如果页面布局发生变化,前端工程师对静态页面进行修改,然后再交给后端工程师,由后端工程师再将该页面改为 JSP 页面 由于上述的问题, JSP 已逐渐退出历史舞台
思考:那为什么我还要介绍和学习JSP
呢?
-
一些公司可能有些老项目还在用
JSP
,所以要求我们必须动JSP
-
我们如果不经历这些复杂的过程,就不能体现后面阶段开发的简单
接下来我们来学习第三阶段,使用 EL表达式
和 JSTL
标签库替换 JSP
中的 Java
代码。
5.EL 表达式
5.1 概述
EL(全称Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。
EL 表达式的主要作用是 获取数据。其实就是从域对象中获取数据,然后将数据展示在页面上。
而 EL 表达式的语法也比较简单,**${expression}** 。 例子:
${name}:获取域中存储的key为name的数据
可以理解为从map集合中获取key为name对应的value值
5.2 代码演示
- 定义servlet,在 servlet 中封装一些数据并存储到 request 域对象中并转发到
03el.jsp
页面。-
创建ELServlet
package com.zbbmeta.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/el")
public class ELServlet extends HttpServlet {@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //get请求 // 1.准备数据 String name = "张三"; String address = "广东省广州市天河区"; Integer age = 30; // 2.存储数据到request域中 request.setAttribute("name", name); request.setAttribute("address", address); request.setAttribute("age", age); // 3.转发到jsp request.getRequestDispatcher("03el.jsp").forward(request, response); // request.getRequestDispatcher("/04if.jsp").forward(request, response); // request.getRequestDispatcher("/05foreach.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //post请求 doGet(request, response); }
}
-
注意:此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享
-
在
03el.jsp
中通过 EL表达式 获取数据<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html> <head> <title>EL</title> </head> <body> <%--取出request存储的数据--%> 姓名: ${name}
地址: ${address}
年龄: ${age}
不存在的: ${address1}
</body> </html> -
在浏览器的地址栏输入
http://localhost:8080/jsp/el
,页面效果如下:
5.3 域对象
JavaWeb中有四大域对象,分别是:
-
page:当前页面有效
-
request:当前请求有效
-
session:当前会话有效
-
application:当前应用有效
el 表达式获取数据,会依次从这4个域中寻找,直到找到为止。而这四个域对象的作用范围如下图所示
例如: ${name},el 表达式获取数据,会先从page域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据,,如果application中没有获取到数据,也不会报错。
6.JSTL标签
6.1 概述
JSP标准标签库(Jsp Standarded Tag Library) ,使用标签取代JSP页面上的Java代码。如下代码就是JSTL标签
<c:if test="${flag == 1}">
男
</c:if>
<c:if test="${flag == 2}">
女
</c:if>
上面代码看起来是不是比 JSP 中嵌套 Java 代码看起来舒服好了。而且前端工程师对标签是特别敏感的,他们看到这段代码是能看懂的。
JSTL 提供了很多标签,这里只对两个最常用的标签进行讲解,<c:forEach>
标签和 <c:if>
标签。
JSTL 使用也是比较简单的,分为如下步骤:
-
导入坐标
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> -
在JSP页面上引入JSTL标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-
使用标签
6.2 if 标签
<c:if>
:相当于 if 判断
-
属性:test,用于定义条件表达式
<c:if test="true">
true
</c:if>
<c:if test="false">
false
</c:if>
代码演示:
-
定义一个
IFServlet
,在该IFServlet
中向 request 域对象中添加共享数据package com.zbbmeta.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/ifservlet")
public class IFServlet extends HttpServlet {@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //get请求 // 1.准备数据 String name = "张三"; String address = "广东省广州市天河区"; Integer age = 30; // 2.存储数据到request域中 request.setAttribute("name", name); request.setAttribute("address", address); request.setAttribute("age", age); // 3.转发到jsp request.getRequestDispatcher("04if.jsp").forward(request, response); // request.getRequestDispatcher("/05foreach.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //post请求 doGet(request, response); }
}
-
定义
04if.jsp
页面,在该页面使用<c:if>
标签<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html> <head> <title>JSTL的if标签</title> </head> <body> <%-- c:if:来完成逻辑判断,替代java中的if --%> <%--c:if 条件直接使用boolean类型--%> <c:if test="true"> true </c:if> <c:if test="false"> false </c:if>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
</body> </html><%--c:if 条件从作用域中取出数据比较, 要访问 /demo01 这个Servlet, Servlet会保存age到request中,这边才能取到age--%> <c:if test="${age < 18}"> 未成年 </c:if> <c:if test="${age >= 18}"> 成年 </c:if>
注意: 在该页面已经要引入 JSTL核心标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 在浏览器中输入
http://localhost:8080/jsp/ifservlet
,显示结果
6.3 forEach 标签
<c:forEach>
:相当于 for 循环。java中有增强for循环和普通for循环,JSTL 中的<c:forEach>
也有两种用法
6.3.1 用法一
类似于 Java 中的增强for循环。涉及到的 <c:forEach>
中的属性如下
-
items:被遍历的容器
-
var:遍历产生的临时变量
-
varStatus:遍历状态对象
如下代码,是从域对象中获取名为 students 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 student
,是 Student对象。在循环里面使用 EL表达式获取每一个Brand对象的属性值
<c:forEach items="${students}" var="student" varStatus="sta">
<tr align="center">
<td>${sta.count}</td>
<td>${student.name}</td>
<td>${student.address}</td>
<td>${student.age}</td>
<c:if test="${student.status==1}">
<td>启用</td>
</c:if>
<c:if test="${student.status==0}">
<td>禁用</td>
</c:if>
<td><a href="#">修改</a> <a href="#">删除</a></td>
</tr>
</c:forEach>
代码演示:
-
定义一个实体类
Student
。package com.zbbmeta.entity;
public class Student {
private String name;
private String address;
private Integer age;private Integer status; public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Student(String name, String address, Integer age, Integer status) { this.name = name; this.address = address; this.age = age; this.status = status; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address='" + address + '\'' + ", age=" + age + ", status=" + status + '}'; }
}
-
定义一个Servlet类命名为
ForeachServlet
。package com.zbbmeta.servlet;
import com.zbbmeta.entity.Student;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@WebServlet("/forservlet")
public class ForeachServlet extends HttpServlet {@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //get请求 // 1.准备数据 List<Student> students = new ArrayList<>(); students.add(new Student("王五","广东省广州市天河区",30,0)); students.add(new Student("李四","广东省江门市",40,1)); students.add(new Student("老刘","广东省珠海市香洲区",60,0)); // 2.存储数据到request域中 request.setAttribute("students", students); // 3.转发到jsp request.getRequestDispatcher("/05foreach.jsp").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //post请求 doGet(request, response); }
}
-
定义名为
05foreach.jsp
页面,内容如下:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html> <head> <title>JSTL的foreach标签</title> </head> <body>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
序号 姓名 地址 年龄 操作 ${sta.count} ${student.name} ${student.address} ${student.age} <c:if test="${student.status==1}">启用 </c:if> <c:if test="${student.status==0}">禁用 </c:if>修改 删除 -
访问浏览器地址:
http://localhost:8080/jsp/forservlet
显示结果:
6.3.2 用法二
类似于 Java 中的普通for循环。涉及到的 <c:forEach>
中的属性如下
-
begin:开始数
-
end:结束数
-
step:步长
实例代码:
从0循环到10,变量名是 i
,每次自增1
<hr/>
<%--类似于普通for循环--%>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>
05foreach.jsp完整代码
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSTL的foreach标签</title>
</head>
<body>
<input type="button" value="新增"><br>
<hr>
<table border="1" cellspacing="0" width="70%">
<tr>
<th>序号</th>
<th>姓名</th>
<th>地址</th>
<th>年龄</th>
<th>操作</th>
</tr>
<%--c:forEach标签: 遍历
items属性: 要遍历的数据
var属性: 变量名,保存遍历到元素
varStatus属性: 遍历时的状态
index: 索引,从0开始
count: 计数器,从1开始
--%>
<c:forEach items="${students}" var="student" varStatus="sta">
<tr align="center">
<td>${sta.count}</td>
<td>${student.name}</td>
<td>${student.address}</td>
<td>${student.age}</td>
<c:if test="${student.status==1}">
<td>启用</td>
</c:if>
<c:if test="${student.status==0}">
<td>禁用</td>
</c:if>
<td><a href="#">修改</a> <a href="#">删除</a></td>
</tr>
</c:forEach>
</table>
<hr/>
<%--类似于普通for循环--%>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>
</body>
</html>