Servlet_JSP详解
Servlet + JSP 详解
本章位置:第二阶段 Java 核心框架
前置知识:Servlet、HTML/CSS、JavaBean、HTTP 基础
本章内容:JSP 基础、Servlet 与 JSP 配合、传统 MVC
下一篇:Servlet 交互 / 作用域 / EL / JSTL
学习目标:理解 JSP 是什么、JSP 为什么本质上还是 Servlet、JSP 的基本语法与内置对象、Servlet 如何向 JSP 传递数据,以及传统 Java Web 中 Servlet + JSP 的 MVC 分工。
一、为什么学习 JSP
上一章我们已经会:
浏览器
↓
Tomcat
↓
Servlet
↓
Service
↓
Mapper
↓
数据库
也已经会在 Servlet 中:
response
.getWriter()
.write(
"<h1>Hello</h1>"
);
但是如果页面越来越复杂:
<table>
...
</table>
全部写成:
writer.write(...)
会非常痛苦。
二、Servlet 拼 HTML 的问题
例如:
response
.getWriter()
.write(
"<html>"
+ "<body>"
+ "<h1>学生列表</h1>"
+ "</body>"
+ "</html>"
);
问题:
HTML 和 Java 混在一起
代码难看
前端页面难维护
标签容易拼错
不方便写 CSS
不方便设计页面
于是出现:
JSP
三、JSP 是什么
JSP:
JavaServer Pages
中文可以理解:
Java 服务器页面
它允许我们在:
HTML 页面
中嵌入一些:
Java 相关表达式和标签
四、JSP 的核心作用
传统 Java Web 中,
JSP 主要负责:
页面展示
Servlet 主要负责:
请求处理
业务调度
所以:
Servlet
Controller
JSP
View
五、Servlet + JSP 基本流程
例如学生列表:
浏览器
↓
/student/list
↓
StudentListServlet
↓
调用 Service
↓
得到 List<Student>
↓
request.setAttribute
↓
forward
↓
student-list.jsp
↓
生成 HTML
↓
浏览器
六、JSP 文件放在哪里
Maven Web 项目常见:
src/main/webapp
例如:
src/main/webapp
├─ index.jsp
├─ login.jsp
└─ WEB-INF
└─ views
└─ student-list.jsp
七、为什么 JSP 常放 WEB-INF
如果放:
WEB-INF/views
浏览器不能直接访问。
例如:
/WEB-INF/views/student-list.jsp
不能直接在浏览器地址栏打开。
只能:
服务器内部转发
八、为什么这样更合理
如果用户可以直接访问 JSP:
可能绕过 Servlet
例如:
没有经过权限判断
没有加载数据
没有执行业务逻辑
所以推荐:
浏览器先访问 Servlet
↓
Servlet 再 forward JSP
九、第一个 JSP
创建:
hello.jsp
内容:
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
Hello JSP
</title>
</head>
<body>
<h1>
Hello JSP
</h1>
</body>
</html>
十、JSP 本质是什么
这是最重要的知识点之一:
JSP 最终会被 Tomcat 翻译成 Servlet。
也就是说:
JSP
不是完全独立的新运行机制
而是:
JSP
↓
Tomcat 翻译
↓
Java Servlet 源码
↓
编译成 class
↓
运行
十一、JSP 请求执行流程
第一次访问:
浏览器请求 JSP
↓
Tomcat 找到 JSP
↓
JSP 翻译成 Java Servlet
↓
编译成 .class
↓
创建 Servlet 对象
↓
执行
↓
返回 HTML
后续访问:
如果 JSP 没修改
一般直接使用编译后的 Servlet
十二、为什么 JSP 第一次可能稍慢
因为第一次访问可能需要:
翻译 JSP
编译 Java
加载 class
创建 Servlet
所以第一次可能比后续访问慢。
十三、JSP 翻译后的类
Tomcat 会把 JSP 翻译成一个类似:
public final class xxx_jsp
extends HttpJspBase {
}
的类。
本质仍然属于:
Servlet 体系
十四、JSP 生命周期
JSP 生命周期和 Servlet 很像。
大概:
JSP 翻译
↓
编译
↓
加载
↓
实例化
↓
jspInit()
↓
_jspService()
↓
jspDestroy()
十五、jspInit()
类似:
Servlet.init()
通常:
只执行一次
十六、_jspService()
每次 JSP 请求:
主要执行这个方法
而且:
通常不需要也不能自己重写
JSP 页面内容最终会被放入:
_jspService()
中。
十七、jspDestroy()
类似:
Servlet.destroy()
JSP 销毁时调用。
十八、JSP 三类传统脚本元素
传统 JSP 中有:
声明
脚本片段
表达式
分别:
<%! ... %>
<% ... %>
<%= ... %>
十九、JSP 声明
语法:
<%!
int count = 0;
%>
它会被翻译成:
JSP Servlet 的成员变量或成员方法
二十、JSP 声明的线程安全风险
例如:
<%!
int count = 0;
%>
多个请求可能共享:
同一个 JSP Servlet 对象
所以:
成员变量可能有线程安全问题
因此现代开发:
尽量不要在 JSP 中写成员变量
二十一、JSP 脚本片段
<%
String name =
"张三";
out.println(
name
);
%>
这段代码会放进:
_jspService()
内部。
二十二、JSP 表达式
<%= "Hello" %>
等价于:
把结果输出到页面
例如:
<%= request.getAttribute("name") %>
二十三、传统脚本为什么不推荐
如果大量使用:
<%
...
%>
页面会变成:
HTML
+
Java
+
业务逻辑
非常混乱。
后来会使用:
EL
JSTL
减少 Java 脚本。
二十四、学习 JSP 脚本的目的
不是让你以后大量使用,
而是为了理解:
JSP 本质
JSP 如何执行 Java
旧项目代码
现代 JSP 推荐:
Servlet + EL + JSTL
二十五、JSP 指令
JSP 常见指令:
page
include
taglib
语法:
<%@ ... %>
二十六、page 指令
例如:
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
常见属性:
contentType
pageEncoding
import
errorPage
isErrorPage
session
二十七、contentType
例如:
contentType="text/html;charset=UTF-8"
告诉浏览器:
响应内容是 HTML
字符编码 UTF-8
二十八、pageEncoding
pageEncoding="UTF-8"
表示:
JSP 文件自身编码
二十九、import
例如:
<%@ page
import="java.util.List"
%>
类似 Java:
import java.util.List;
三十、为什么 JSP 中尽量少 import 业务类
如果页面里大量:
<%@ page import="..." %>
然后写大量 Java,
说明:
MVC 分层可能已经混乱
更推荐:
Servlet 准备数据
JSP 只展示
三十一、include 指令
<%@ include
file="/common/header.jsp"
%>
属于:
静态包含
三十二、静态包含
可以理解:
JSP 翻译阶段
把多个文件合成一个
常用于:
公共头部
公共导航
公共底部
三十三、动态包含
还可以:
<jsp:include
page="/common/header.jsp"
/>
这是:
运行时包含
三十四、静态 include 和动态 include
简单理解:
<%@ include %>
翻译阶段合并
<jsp:include>
运行阶段包含
三十五、JSP 注释
JSP 注释:
<%--
JSP 注释
--%>
这类注释:
服务器处理时就会去掉
浏览器看不到。
三十六、HTML 注释
<!-- HTML 注释 -->
服务器通常仍会输出到浏览器。
用户查看页面源码:
可能看得到
三十七、Java 注释
JSP 脚本中:
<%
// Java 注释
%>
也是 Java 层面的注释。
三十八、JSP 九大内置对象
传统 JSP 中有九大内置对象:
request
response
session
application
out
pageContext
config
page
exception
三十九、request
类型:
HttpServletRequest
就是:
当前请求对象
例如:
<%= request.getParameter("name") %>
四十、response
类型:
HttpServletResponse
就是:
当前响应对象
例如:
<%
response.setStatus(200);
%>
但一般不建议 JSP 做复杂响应控制。
四十一、session
类型:
HttpSession
表示:
当前用户会话
后面作用域章节详细讲。
四十二、application
本质:
ServletContext
表示:
整个 Web 应用
四十三、out
类型:
JspWriter
用于:
向响应中输出内容
例如:
<%
out.println(
"Hello"
);
%>
四十四、pageContext
类型:
PageContext
是 JSP 中非常重要的对象。
可以:
访问多个作用域
获取 request
获取 response
获取 session
获取 application
转发
包含
四十五、config
类型:
ServletConfig
表示:
JSP 对应 Servlet 的配置
四十六、page
相当于:
当前 JSP Servlet 对象
类似:
this
四十七、exception
只有页面配置:
isErrorPage="true"
时通常可以使用。
表示:
当前异常对象
四十八、为什么 JSP 有这些内置对象
因为 JSP 最终翻译成 Servlet,
Tomcat 在生成 _jspService() 时已经帮我们准备好了:
request
response
session
application
out
...
所以 JSP 中直接可以用。
四十九、Servlet 如何传数据给 JSP
Servlet:
request.setAttribute(
"name",
"张三"
);
然后:
request
.getRequestDispatcher(
"/WEB-INF/views/user.jsp"
)
.forward(
request,
response
);
JSP:
<%= request.getAttribute("name") %>
五十、request attribute 是核心桥梁
传统 MVC 中:
Servlet
负责准备数据
request
负责携带数据
JSP
负责展示数据
五十一、传 JavaBean
Servlet:
Student student =
studentService.findById(
1L
);
request.setAttribute(
"student",
student
);
JSP:
<%
Student student =
(Student)
request.getAttribute(
"student"
);
%>
然后:
<%= student.getName() %>
五十二、为什么这种 JSP 写法不够好
因为页面中又开始:
import Java 类
强制类型转换
调用 Getter
写 Java 脚本
所以后面 EL 会改成:
${student.name}
更简洁。
五十三、传 List
Servlet:
List<Student> students =
studentService.findAll();
request.setAttribute(
"students",
students
);
传统 JSP:
<%
List<Student> students =
(List<Student>)
request.getAttribute(
"students"
);
%>
五十四、传统 for 循环展示
<%
for (
Student student :
students
) {
%>
<p>
<%= student.getName() %>
</p>
<%
}
%>
这种代码虽然能运行,
但:
可读性比较差
五十五、后面 JSTL 会怎么写
后面可以变成:
<c:forEach
items="${students}"
var="student"
>
<p>
${student.name}
</p>
</c:forEach>
更接近:
模板页面
五十六、Servlet + JSP 最基本 MVC
例如:
StudentListServlet
↓
studentService.findAll()
↓
request.setAttribute("students", list)
↓
forward
↓
student-list.jsp
这是传统 MVC 最常见模式。
五十七、Model 是什么
MVC 中:
Model
负责:
数据
业务状态
业务逻辑
例如:
Student
StudentService
StudentMapper
五十八、View 是什么
View:
负责展示
传统 Java Web:
JSP
五十九、Controller 是什么
Controller:
负责接收请求
调用业务
选择页面
传统 Java Web:
Servlet
六十、Servlet 和 JSP 不应该互相抢职责
错误:
Servlet 拼大量 HTML
JSP 直接访问数据库
推荐:
Servlet
做控制
Service
做业务
Mapper
做数据库
JSP
做展示
六十一、完整学生列表流程
浏览器访问:
/student/list
Servlet:
@WebServlet(
"/student/list"
)
public class StudentListServlet
extends HttpServlet {
private final StudentService
studentService =
new StudentService();
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response
)
throws ServletException,
IOException {
List<Student> students =
studentService
.findAll();
request.setAttribute(
"students",
students
);
request
.getRequestDispatcher(
"/WEB-INF/views/student-list.jsp"
)
.forward(
request,
response
);
}
}
六十二、student-list.jsp
传统脚本版:
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
<%@ page
import="java.util.List"
%>
<%@ page
import="com.example.entity.Student"
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
学生列表
</title>
</head>
<body>
<h1>
学生列表
</h1>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>专业</th>
</tr>
</thead>
<tbody>
<%
List<Student> students =
(List<Student>)
request.getAttribute(
"students"
);
if (
students != null
) {
for (
Student student :
students
) {
%>
<tr>
<td>
<%= student.getId() %>
</td>
<td>
<%= student.getStudentNo() %>
</td>
<td>
<%= student.getName() %>
</td>
<td>
<%= student.getAge() %>
</td>
<td>
<%= student.getMajor() %>
</td>
</tr>
<%
}
}
%>
</tbody>
</table>
</body>
</html>
六十三、这个案例虽然能用,但不够现代
因为 JSP 中包含:
Java import
类型转换
for 循环
Java 方法调用
后面会继续优化为:
EL
+
JSTL
六十四、登录页面案例
login.jsp:
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
登录
</title>
</head>
<body>
<form
action="<%= request.getContextPath() %>/login"
method="post"
>
<div>
<label>
用户名
</label>
<input
type="text"
name="username"
>
</div>
<div>
<label>
密码
</label>
<input
type="password"
name="password"
>
</div>
<button
type="submit"
>
登录
</button>
</form>
</body>
</html>
六十五、为什么 action 要加 contextPath
假设项目部署路径:
/demo
直接:
action="/login"
可能请求:
http://localhost:8080/login
而不是:
http://localhost:8080/demo/login
所以:
<%= request.getContextPath() %>
非常重要。
六十六、Servlet 登录处理
@WebServlet(
"/login"
)
public class LoginServlet
extends HttpServlet {
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
)
throws IOException,
ServletException {
request.setCharacterEncoding(
"UTF-8"
);
String username =
request.getParameter(
"username"
);
String password =
request.getParameter(
"password"
);
if (
"admin".equals(
username
)
&&
"123456".equals(
password
)
) {
request.setAttribute(
"username",
username
);
request
.getRequestDispatcher(
"/WEB-INF/views/success.jsp"
)
.forward(
request,
response
);
return;
}
request.setAttribute(
"message",
"用户名或密码错误"
);
request
.getRequestDispatcher(
"/login.jsp"
)
.forward(
request,
response
);
}
}
六十七、登录失败为什么用 forward
因为我们想保留:
request.message
让 login.jsp 显示错误。
如果重定向:
会新建 request
原 attribute 丢失。
六十八、success.jsp
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
<!DOCTYPE html>
<html>
<body>
<h1>
登录成功
</h1>
<p>
欢迎:
<%= request.getAttribute("username") %>
</p>
</body>
</html>
六十九、JSP 页面编码
建议统一:
UTF-8
JSP:
<%@ page
contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"
%>
HTML:
<meta charset="UTF-8">
项目源文件也使用:
UTF-8
七十、JSP 中文乱码排查
如果 JSP 中文乱码,检查:
JSP pageEncoding
response contentType
项目文件编码
IDEA Encoding
Tomcat URIEncoding
请求参数编码
七十一、pageEncoding 和 response 编码不是同一件事
pageEncoding
JSP 源文件怎么读
contentType charset
响应给浏览器时怎么编码
两者都建议:
UTF-8
七十二、JSP 编译错误
如果 JSP 写错 Java 语法:
<%
int a =
%>
访问页面时可能看到:
500
JasperException
因为 JSP 翻译/编译失败。
七十三、什么是 Jasper
Tomcat 中负责 JSP:
翻译和编译
的组件通常叫:
Jasper
所以 JSP 报错常看到:
JasperException
七十四、JSP 404
JSP 404 常见:
路径错
文件不存在
项目没部署
Context Path 错
WEB-INF 页面被浏览器直接访问
七十五、WEB-INF 页面直接访问为什么 404
例如:
http://localhost:8080/demo/WEB-INF/views/a.jsp
浏览器不能直接访问。
应该:
Servlet forward
七十六、JSP 500
常见:
JSP Java 代码编译错误
NullPointerException
类型转换错误
import 错误
对象不存在
优先看:
Tomcat 控制台完整堆栈
七十七、request.getAttribute 返回 Object
例如:
Object value =
request.getAttribute(
"student"
);
所以传统 JSP 需要:
(Student) value
七十八、强制类型转换风险
如果实际放的是:
User
你却:
(Student)
会:
ClassCastException
这也是为什么 EL 更方便。
七十九、request 参数和 attribute 再复习
参数:
request.getParameter(
"name"
);
通常来自:
客户端
attribute:
request.getAttribute(
"name"
);
通常来自:
服务器自己 setAttribute
八十、JSP 中不要直接 new Service
不推荐:
<%
StudentService service =
new StudentService();
List<Student> list =
service.findAll();
%>
这会让 JSP:
承担业务控制
破坏 MVC。
八十一、JSP 中更不要直接操作 MyBatis
错误:
<%
SqlSession session = ...
%>
页面不应该直接:
访问数据库
八十二、JSP 页面只应该做什么
推荐:
读取已经准备好的展示数据
输出 HTML
少量展示判断
少量循环展示
后面通过:
EL + JSTL
完成。
八十三、Servlet 决定跳哪个 JSP
例如:
if (
success
) {
forward(
success.jsp
);
} else {
forward(
error.jsp
);
}
这体现:
Controller 决定 View
八十四、JSP 不应该决定复杂业务流程
例如:
余额够不够
订单能不能支付
用户有没有权限
这些属于:
Service / Controller
不是 JSP。
八十五、JSP 表达式和输出转义问题
例如:
<%= request.getParameter("name") %>
如果用户输入:
<script>
...
</script>
直接输出可能造成:
XSS
八十六、为什么后面 JSTL c:out 更安全
后面可以:
<c:out
value="${name}"
/>
默认可以对特殊字符做:
HTML 转义
比直接:
<%= ... %>
更安全。
八十七、传统 JSP 安全原则
不要:
直接输出不可信用户 HTML
尤其:
评论
昵称
留言
搜索关键词
应该:
转义
八十八、JSP 动态包含案例
header.jsp:
<header>
<h1>
学生管理系统
</h1>
</header>
页面:
<jsp:include
page="/common/header.jsp"
/>
八十九、公共页面拆分
常见:
header.jsp
footer.jsp
sidebar.jsp
navbar.jsp
减少:
重复 HTML
九十、JSP Forward 标签简单了解
JSP 也支持:
<jsp:forward
page="/login.jsp"
/>
但真正控制请求流:
更推荐 Servlet
九十一、JSP useBean 简单了解
传统 JSP 标签:
<jsp:useBean
id="student"
class="com.example.Student"
scope="request"
/>
可以创建/获取 Bean。
但现代开发中:
使用频率较低
了解即可。
九十二、jsp:setProperty
传统:
<jsp:setProperty
name="student"
property="name"
value="张三"
/>
九十三、jsp:getProperty
<jsp:getProperty
name="student"
property="name"
/>
九十四、为什么这些标签现在不常用
因为后来:
EL
JSTL
Spring MVC
提供了更清晰的方式。
九十五、JSP 四大作用域预览
JSP 中常见四个作用域:
page
request
session
application
详细放到下一篇。
九十六、page 作用域
当前 JSP 页面有效。
通过:
pageContext
管理。
九十七、request 作用域
一次请求有效。
例如:
request.setAttribute(
"student",
student
);
转发后仍然能使用。
九十八、session 作用域
同一用户会话中有效。
常用于:
登录用户
购物车
会话状态
九十九、application 作用域
整个 Web 应用共享。
本质:
ServletContext
一百、为什么 request 最适合 Servlet → JSP
因为:
展示数据通常只需要当前请求
例如:
学生列表
商品详情
查询结果
没必要放:
session
application
一百零一、不要滥用 session
如果所有数据都放 session:
内存占用增加
数据容易过期
状态变复杂
多页面互相影响
应该根据:
生命周期
选择正确作用域。
一百零二、Servlet + JSP CRUD 架构
例如学生管理:
/student/list
StudentListServlet
/student/add
StudentAddServlet
/student/edit
StudentEditServlet
/student/delete
StudentDeleteServlet
页面:
student-list.jsp
student-form.jsp
一百零三、列表页面流程
GET /student/list
↓
StudentListServlet
↓
service.findAll()
↓
request.setAttribute
↓
forward student-list.jsp
一百零四、新增页面流程
GET:
/student/add-page
↓
forward form.jsp
POST:
/student/add
↓
读取参数
↓
Service 新增
↓
redirect /student/list
一百零五、编辑流程
GET:
/student/edit?id=1
↓
查询 Student
↓
request.setAttribute
↓
forward form.jsp
POST:
/student/update
↓
读取表单
↓
更新
↓
redirect list
一百零六、删除流程
POST /student/delete
↓
读取 id
↓
删除
↓
redirect list
不建议用普通 GET 做危险删除操作。
一百零七、为什么删除更适合 POST/DELETE
GET 应该:
主要读取资源
删除属于:
改变服务器状态
所以不要:
用户点一个 GET 链接就直接删除
一百零八、JSP 表单回显
编辑页面要显示已有值:
<input
name="name"
value="<%= student.getName() %>"
>
后面 EL:
value="${student.name}"
更简洁。
一百零九、表单错误回显
新增失败:
request.setAttribute(
"message",
"学号已存在"
);
request.setAttribute(
"student",
student
);
forward(
form.jsp
);
这样页面可以:
显示错误
保留用户输入
一百一十、什么时候失败用 forward
如果希望保留:
当前 request 数据
表单输入
错误信息
通常:
forward
比较方便。
一百一十一、什么时候成功用 redirect
新增成功后:
redirect 到列表
防止:
刷新重复提交
一百一十二、MVC 完整结构
Browser
↓
Servlet Controller
↓
Service
↓
Mapper
↓
MySQL
↓
Controller
↓
request attribute
↓
JSP View
↓
HTML
↓
Browser
一百一十三、传统 Java Web 包结构
com.example
├─ entity
├─ mapper
├─ service
├─ servlet
└─ util
页面:
webapp
└─ WEB-INF
└─ views
一百一十四、为什么 Servlet 类不要叫 XxxPage
更推荐:
UserListServlet
UserAddServlet
LoginServlet
名称表达:
处理什么请求
一百一十五、JSP 命名建议
例如:
login.jsp
student-list.jsp
student-form.jsp
student-detail.jsp
做到:
见名知意
一百一十六、常见错误:JSP 里写数据库连接
错误:
JSP
→ JDBC / MyBatis
正确:
JSP
只展示
Servlet / Service
负责逻辑
一百一十七、常见错误:Servlet 不 forward
Servlet:
request.setAttribute(
"students",
list
);
然后没有:
forward
浏览器不会自动跳到 JSP。
一百一十八、常见错误:forward 路径写错
推荐:
request
.getRequestDispatcher(
"/WEB-INF/views/student-list.jsp"
)
路径:
以 Web 应用根目录为基准
一百一十九、常见错误:sendRedirect 到 WEB-INF
错误:
response.sendRedirect(
"/WEB-INF/views/a.jsp"
);
因为重定向是:
浏览器重新请求
浏览器无法直接访问 WEB-INF。
应该:
forward
一百二十、forward 可以进入 WEB-INF
因为:
服务器内部转发
所以:
forward(
"/WEB-INF/views/a.jsp"
)
是可以的。
一百二十一、常见错误:request attribute 名写错
Servlet:
request.setAttribute(
"students",
list
);
JSP:
request.getAttribute(
"studentList"
);
结果:
null
名称必须一致。
一百二十二、常见错误:request 被重定向后丢失
Servlet:
request.setAttribute(
"message",
"成功"
);
response.sendRedirect(
"/result.jsp"
);
result.jsp:
拿不到 message
因为:
重定向是新请求
一百二十三、常见错误:页面直接访问导致数据为空
你直接打开:
/student-list.jsp
但没有经过:
StudentListServlet
所以 request 中:
没有 students
这也是为什么推荐放:
WEB-INF
一百二十四、常见错误:JSP import 类找不到
可能:
类包名错误
Java 类没有编译
项目部署旧版本
Artifact 没更新
一百二十五、常见错误:JSP 修改后没生效
检查:
Tomcat 是否重新部署
IDEA Artifact 更新
浏览器缓存
JSP 是否改对项目
一百二十六、常见错误:JSP 500 JasperException
优先看:
Root Cause
Caused by
具体 JSP 行号
不要只看:
最上面一行错误
一百二十七、JSP 编译后文件在哪里
Tomcat 通常会在:
work
目录生成 JSP 对应 Java 和 class。
如果使用 IDEA:
CATALINA_BASE
可能是 IDEA 临时目录。
所以编译后的 JSP 文件未必在:
Tomcat 安装目录
一百二十八、为什么 CATALINA_BASE 很重要
如果 IDEA 使用自己的:
CATALINA_BASE
那:
work
logs
conf
webapps
等运行文件可能在:
IDEA 创建的实例目录
而不是 CATALINA_HOME。
一百二十九、JSP 与前后端分离
传统:
Servlet + JSP
属于:
服务端渲染
服务器生成:
HTML
返回浏览器。
一百三十、Vue 模式
前后端分离:
Servlet / SpringBoot
返回 JSON
Vue
负责生成页面
流程:
Browser
↓
Vue
↓
HTTP API
↓
Java
↓
JSON
↓
Vue
↓
DOM
一百三十一、为什么还要学 JSP
因为 JSP 能帮助理解:
服务端渲染
MVC
request 转发
作用域
EL
JSTL
传统 Java Web
这些概念后面 Spring MVC 仍然有对应思想。
一百三十二、JSP 与 Spring MVC View
Spring MVC 也可以:
Controller
↓
Model
↓
JSP
只是现代项目更多:
JSON + Vue
一百三十三、JSP 不等于过时到完全没用
需要客观看:
新项目中使用减少
老项目中仍常见
学习 MVC 很有价值
考试和课程仍可能要求
一百三十四、练习题 1:Hello JSP
创建:
hello.jsp
显示:
Hello JSP
一百三十五、练习题 2:JSP 脚本元素
分别练习:
<%! %>
<% %>
<%= %>
观察:
它们分别被翻译到哪里
一百三十六、练习题 3:Servlet 传字符串
Servlet:
request.setAttribute(
"message",
"欢迎学习 JSP"
);
forward JSP。
JSP:
输出 message
一百三十七、练习题 4:Servlet 传 Student
Servlet:
查询 Student
setAttribute
forward
JSP:
显示 id
name
age
major
一百三十八、练习题 5:Servlet 传 List
查询:
List<Student>
JSP 用传统:
for
输出表格。
一百三十九、练习题 6:登录页面
实现:
login.jsp
LoginServlet
success.jsp
要求:
POST 登录
失败 forward
成功 forward 或 redirect
并理解区别。
一百四十、练习题 7:公共 header
创建:
header.jsp
分别使用:
<%@ include %>
<jsp:include>
观察效果。
一百四十一、练习题 8:WEB-INF
把 JSP 放:
WEB-INF/views
测试:
浏览器直接访问失败
Servlet forward 成功
一百四十二、练习题 9:学生列表 MVC
完成:
StudentListServlet
StudentService
StudentMapper
student-list.jsp
完整展示数据库学生。
一百四十三、练习题 10:新增后重定向
POST:
/student/add
成功后:
redirect /student/list
验证:
刷新列表不会重复新增
一百四十四、必须掌握的 JSP 语法
<%@ page %>
<%@ include %>
<%@ taglib %>
<%! %>
<% %>
<%= %>
<%-- --%>
一百四十五、必须掌握内置对象
request
response
session
application
out
pageContext
config
page
exception
一百四十六、必须掌握 Servlet + JSP 配合
request.setAttribute(
"key",
value
);
request
.getRequestDispatcher(
"/WEB-INF/views/a.jsp"
)
.forward(
request,
response
);
一百四十七、必须回答的问题
学完后应该能够回答:
1. JSP 是什么?
2. JSP 为什么本质上还是 Servlet?
3. JSP 第一次访问为什么可能较慢?
4. JSP 生命周期是什么?
5. <% %> 是什么?
6. <%= %> 是什么?
7. <%! %> 是什么?
8. JSP 九大内置对象有哪些?
9. request 和 session 分别是什么?
10. 为什么 JSP 常放 WEB-INF?
11. Servlet 如何把数据传给 JSP?
12. request.setAttribute 和 getParameter 有什么区别?
13. forward 和 redirect 对 JSP 有什么影响?
14. Servlet + JSP 中 MVC 如何分工?
15. JSP 中为什么不应该直接操作数据库?
16. 为什么后面要学习 EL 和 JSTL?
17. JSP 500 JasperException 怎么排查?
一百四十八、Servlet + JSP 知识结构
Servlet + JSP
│
├─ Servlet
│ ├─ 接收请求
│ ├─ 获取参数
│ ├─ 调用 Service
│ ├─ setAttribute
│ └─ forward
│
├─ JSP
│ ├─ page 指令
│ ├─ 脚本元素
│ ├─ 内置对象
│ ├─ include
│ └─ 页面展示
│
├─ MVC
│ ├─ Model
│ ├─ View
│ └─ Controller
│
├─ 作用域
│ ├─ page
│ ├─ request
│ ├─ session
│ └─ application
│
└─ 页面流转
├─ forward
└─ redirect
一百四十九、从 JSP 到 EL / JSTL
现在我们已经会:
<%
Student student =
(Student)
request.getAttribute(
"student"
);
%>
<%= student.getName() %>
也会:
<%
for (
Student student :
students
) {
%>
...
<%
}
%>
但这种写法:
Java 代码太多
所以后面要学习:
EL
Expression Language
把:
<%= student.getName() %>
改成:
${student.name}
再学习:
JSTL
把 Java for 循环改成:
<c:forEach>
一百五十、本章总结
JSP 最核心的一句话:
JSP 是一种服务端页面技术,最终会被 Tomcat 翻译成 Servlet,用来生成动态 HTML。
Servlet + JSP 最核心分工:
Servlet
负责控制
Service
负责业务
Mapper
负责数据访问
JSP
负责展示
完整流程:
浏览器
↓
Servlet
↓
Service
↓
Mapper
↓
数据库
↓
Servlet
↓
request.setAttribute
↓
forward
↓
JSP
↓
HTML
↓
浏览器
一定要记住:
JSP 本质上仍然是 Servlet
JSP 不应该写复杂业务逻辑
JSP 不应该直接访问数据库
Servlet 不应该拼大量 HTML
WEB-INF 中 JSP 需要服务器内部转发
当前请求展示数据优先放 request
到这里:
Servlet + JSP
这套传统 Java Web MVC 基础已经建立。
下一篇按照课程表继续:
Servlet 交互
作用域
EL
JSTL
重点会解决:
页面怎么更方便取数据
四大作用域怎么选
JSP 怎么彻底减少 Java 脚本
列表和条件怎么用标签完成