JSP
今日目标:
理解 JSP 及 JSP 原理
能在 JSP中使用 EL表达式 和 JSTL标签
1、JSP 概述
JSP(全称:Java Server Pages):Java 服务端页面。是一种动态的网页技术,其中既可以定义 HTML、JS、CSS等静态内容,还可以定义 Java代码的动态内容,也就是 JSP = HTML + Java。如下就是jsp代码。
Hello JSP
你好 JSP
在浏览器地址栏输入:http://localhost:8080/jsp/01_Hello_JSP.jsp 显示结果为:
思考:JSP 能做什么呢?
JSP 作用:简化开发,避免了在Servlet中直接输出HTML标签。
2、JSP 快速入门
接下来我们做一个简单的快速入门代码。
(1)搭建环境
创建一个maven的 web 项目,项目结构如下:
pom.xml 文件内容如下:
4.0.0
com.zbbmeta
07_JSP
1.0-SNAPSHOT
war
11
11
javax.servlet
javax.servlet-api
4.0.1
provided
(2)导入 JSP 依赖
在 dependencies 标签中导入 JSP 的依赖,如下:
javax.servlet.jsp
jsp-api
2.2
provided
(3)创建 jsp 页面
在项目的 webapp 下创建jsp页面。
通过上面方式创建一个名为 hello.jsp 的页面。
(4)编写代码
在 hello.jsp 页面中书写 HTML 标签和 Java 代码,如下:
Hello JSP
你好 JSP
(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-srcjavaorgapachejasperruntime(可自行到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("rn");
out.write("rn");
out.write("rn");
out.write("rn");
out.write(" Hello JSPrn");
out.write("rn");
out.write("rn");
out.write(" 你好 JSPrn");
out.write(" ");
System.out.println("你好 Java代码");
out.write("rn");
out.write("rn");
out.write("rn");
以前我们自己写 servlet 时,现有由Tomcat自动生成这部分代码,这也就是JSP的作用:简化Servlet的开发。
4、JSP 脚本
JSP脚本用于在 JSP页面内定义 Java代码.
(1)JSP 脚本分类
JSP 脚本有如下三个分类:
- :内容会直接放到_jspService()方法之中。
- :内容会放到out.print()中,作为out.print()的参数。
- :内容会放到_jspService()方法之外,被类直接包含 我们主要学习前两种分类。 代码演示:
创建02script.jsp,并书写内容。
通过浏览器访问 02script.jsp 后,查看转换的 _02script_jsp.java 文件,name和listStrs 变量定义在了 _jspService() 方法中。
String name = "张三";
//注意:最好泛型不要省略
List listStrs = new ArrayList();
listStrs.add("王五");
listStrs.add("老六");
listStrs.add("赵四");
在 02script.jsp 中书写。
通过浏览器访问 02script.jsp 后,查看转换的 _02script_jsp.java 文件,该脚本的内容被放在了 out.print() 中,作为参数。
out.print(name);
out.write("rn");
out.write(" ");
out.print(listStrs);
浏览器输出结果:
在 hello.jsp 中书写。
通过浏览器访问 02script.jsp 后,查看转换的 _02script_jsp.java 文件,该脚本的内容被放在了成员位置。
(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 表达式
(1)概述
EL(全称Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。
EL 表达式的主要作用是 获取数据。其实就是从域对象中获取数据,然后将数据展示在页面上。
而 EL 表达式的语法也比较简单,**${expression}** 。 例子:
${name}:获取域中存储的key为name的数据
可以理解为从map集合中获取key为name对应的value值
(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表达式 获取数据。
EL
姓名: ${name}
地址: ${address}
年龄: ${age}
不存在的: ${address1}
- 在浏览器的地址栏输入 http://localhost:8080/jsp/el ,页面效果如下:
(3)域对象
JavaWeb中有四大域对象,分别是:
- page:当前页面有效。
- request:当前请求有效。
- session:当前会话有效。
- application:当前应用有效。
el 表达式获取数据,会依次从这4个域中寻找,直到找到为止。而这四个域对象的作用范围如下图所示:
例如: ${name},el 表达式获取数据,会先从page域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据,,如果application中没有获取到数据,也不会报错。
6、JSTL标签
(1)概述
JSP标准标签库(Jsp Standarded Tag Library) ,使用标签取代JSP页面上的Java代码。如下代码就是JSTL标签。
男
女
上面代码看起来是不是比 JSP 中嵌套 Java 代码看起来舒服好了。而且前端工程师对标签是特别敏感的,他们看到这段代码是能看懂的。
JSTL 提供了很多标签,这里只对两个最常用的标签进行讲解, 标签和 标签。
JSTL 使用也是比较简单的,分为如下步骤:
- 导入坐标
jstl
jstl
1.2
- 在JSP页面上引入JSTL标签库 。
- 使用标签
(2)if 标签
:相当于 if 判断.
- 属性:test,用于定义条件表达式。
true
false
代码演示:
- 定义一个 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 页面,在该页面使用 标签。
JSTL的if标签
true
false
未成年
成年
注意: 在该页面已经要引入 JSTL核心标签库。
。
- 在浏览器中输入http://localhost:8080/jsp/ifservlet,显示结果。
(3)forEach 标签
:相当于 for 循环。java中有增强for循环和普通for循环,JSTL 中的 也有两种用法
用法一
类似于 Java 中的增强for循环。涉及到的 中的属性如下:
- items:被遍历的容器。
- var:遍历产生的临时变量。
- varStatus:遍历状态对象。
如下代码,是从域对象中获取名为 students 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 student,是 Student对象。在循环里面使用 EL表达式获取每一个Brand对象的属性值。
${sta.count}
${student.name}
${student.address}
${student.age}
启用
禁用
修改 删除
代码演示:
- 定义一个实体类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 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 页面,内容如下:
JSTL的foreach标签
序号
姓名
地址
年龄
操作
${sta.count}
${student.name}
${student.address}
${student.age}
启用
禁用
修改 删除
- 访问浏览器地址:http://localhost:8080/jsp/forservlet显示结果:
用法二
类似于 Java 中的普通for循环。涉及到的 中的属性如下
- begin:开始数
- end:结束数
- step:步长
实例代码:
从0循环到10,变量名是 i ,每次自增1。
${i}
05foreach.jsp完整代码。
JSTL的foreach标签
序号
姓名
地址
年龄
操作
${sta.count}
${student.name}
${student.address}
${student.age}
启用
禁用
修改 删除
${i}