跟着狂神学JavaWeb --> 8. JSP
8.1 什么是JSP?
- Java Server Pages : Java服务器端页面;
- 和Servlet一样,也是用于动态Web技术;
- 最大的特点:写JSP,就像再写HTML;
- 与HTML的区别:
- HTML只给用户提供静态数据;
- JSP页面中可以嵌入Java代码,为用户提供动态数据。
8.2 JSP原理
思路:JSP到底是怎么运行的?
-
代码层面没有任何问题:
-
服务器端
-
Tomcat服务器工作的目录:
D:\Tomcat\apache-tomcat-10.0.4\work
-
IDEA中使用Tomcat的会在IDEA的tomcat中生成一个work目录,位置:
C:\Users\用户名\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat
-
C:\Users\用户名\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\Unnamed_javaweb-session-cookie\work\Catalina\localhost\ROOT\org\apache\jsp
-
发现页面转变成了java程序文件!
-
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet
-
JSP最终也会被转变为Java类
-
JSP本质上就是一个Servlet
- 分析原理:
//初始化
public void _jspInit() {}
//销毁
public void _jspDestroy() {}
//JSPService 参数对象:request、response
public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response)
throws java.io.IOException, jakarta.servlet.ServletException {
//1.判断请求
if (!jakarta.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}
//2.内置对象
final jakarta.servlet.jsp.PageContext pageContext; //页面上下文
jakarta.servlet.http.HttpSession session = null; //session
final jakarta.servlet.ServletContext application; //applicationContext
final jakarta.servlet.ServletConfig config; //config 配置
jakarta.servlet.jsp.JspWriter out = null; //out 输出对象
final java.lang.Object page = this; //page当前页
jakarta.servlet.jsp.JspWriter _jspx_out = null;
jakarta.servlet.jsp.PageContext _jspx_page_context = null;
try {
//3.输出页面前的操作,这些对象可以在JSP页面中直接使用
response.setContentType("text/html"); //设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
//4.输出页面
out.write("<html>\n");
out.write("<body>\n");
out.write("<h2>Hello World!</h2>\n");
out.write("</body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof jakarta.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
- 原理流程:
-
在JSP页面中,如果是Java代码,会原封不动的输出;如果是HTML代码,会被转化为out.write(“XXXX”)的格式输出到前端页面。
-
示例如下:
hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--java代码需要写在<% %>中--%>
<%
String name = "ano";
%>
name:<%=name%>
</body>
</html>
运行hello.jsp
:
查看服务器工作目录:
观察hello_jsp.java文件:
8.3 JSP基础语法
- 支持所有Java语法
- JSP表达式
<%--JSP表达式
作用:将程序的输出输出到客户端
<%=变量或者表达式>
--%>
<%=new Date()%>
- JSP脚本片段
<%--JSP脚本片段--%>
<%
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum+=i;
}
out.println("<h1>Sum=" + sum + "</h1>");
%>
- JSP脚本片段再实现
<%
int x = 10;
out.println(x);
%>
<p>这是一个jsp文件</p>
<%
int y = 15;
out.println(y);
%>
<hr>
<%--在java代码中嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
<h2>Hello,ano <%=i%></h2>
<%
}
%>
<%--简化为EL表达式--%>
<% for (int i = 0; i < 5; i++) { %>
<h2>Hello,ano ${i}</h2>
<% } %>
测试结果:
- 分析:
- 以上的示例JSP代码服务器将JSP页面编译为java文件时,这些代码都被写在了
public void _jspService(HttpServletRequest request, response)
方法中,如果想要写在外部呢?即JSP声明。如下: - JSP声明
<%--jsp声明--%>
<%!
static {
System.out.println("Loading Servlet...");
}
private int globalVar = 0;
public void ano() {
System.out.println("进入了ano方法!");
}
%>
- 分析:
-
Java声明会被编译到JSP生成的Java类中,其他的会被生成到该类的_jspService方法中。
-
JSP的注释不会出现在客户端,而HTML的会显示在客户端。
<!--这是一行HTML注释-->
8.4 JSP指令
8.4.1 @page指令
<%@page args...... %>
- 自定义错误页面的示例代码:
500.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>自定义500错误页面</h2>
<img src="../img/500.png" alt="500">
</body>
</html>
400.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>自定义404错误页面</h2>
<img src="${pageContext.request.contextPath}/img/404.png" alt="404">
</body>
</html>
- 第一种方式:
<%@page errorPage="error/500.jsp" %>
测试代码:
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--定制错误页面--%>
<%@page errorPage="error/500.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int x = 1/0;
%>
</body>
</html>
测试结果如下:
- 第二种方式:在
web.xml
中配置如下:
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
测试结果:
8.4.2 @include指令
<%@include file="" %>
- 给网页添加头部和底部的示例代码:
header.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h3>This is Header</h3>
footer.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h3>This is Footer</h3>
测试代码如test2.jsp
,有两种方式:
- 使用
@include
指令 - 使用
jsp:include
标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%@include file="common/header.jsp"%>
<h2>网页主体</h2>
<%@include file="common/footer.jsp"%>
<hr>
<%--jsp标签--%>
<jsp:include page="/common/header.jsp"/>
<h2>网页主体2</h2>
<jsp:include page="/common/footer.jsp"/>
</body>
</html>
测试结果:
- 两种方式的区别如下:
@include
指令的方式本质是合并页面为一个jsp:include
标签的方式本质是拼接页面
8.5 JSP的9大内置对象及作用域
8.5.1 内置对象
pageContext
:存东西request
:存东西response
session
:存东西application
: ServletContext,存东西config
: ServletConfigout
: JspWriterpage
: 几乎不用exception
8.5.2 作用域
pageContext.setAttribute("name1","ano1"); //保存的数据只在一个页面有效
request.setAttribute("name2","ano2"); //保存的数据旨在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","ano3"); //保存的数据在一次会话中有效,即从打开浏览器到关闭浏览器
application.setAttribute("name4","ano4"); //保存的数据旨在服务器中有效,即从打开服务器到关闭服务器
- 示意图理解:
- request:客户端向服务器发送请求,产生的数据,用户看完就没有用了,比如:新闻;
- session: 客户端向服务器发送请求,产生的数据,用户看完后,一会还有用,比如:购物车;
- application: 客户端向服务器发送请求,产生的数据,一个用户看完后,其他用户还可能使用,比如:聊天数据。
pageContextDemo01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
pageContext.setAttribute("name1","ano1"); //保存的数据只在一个页面有效
request.setAttribute("name2","ano2"); //保存的数据旨在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","ano3"); //保存的数据在一次会话中有效,即从打开浏览器到关闭浏览器
application.setAttribute("name4","ano4"); //保存的数据旨在服务器中有效,即从打开服务器到关闭服务器
%>
<%
//从内置对象pageContext取出
//通过get方式只能自己去自己的值
// String name1 = (String)pageContext.getAttribute("name1");
//从内置对象pageContext取出,通过寻找的方式取值
//原理:从底层到高层(作用域):pageContext -> request -> session -> application
//JVM:双亲委派机制
String name1 = (String)pageContext.findAttribute("name1"); //ano1
String name2 = (String)pageContext.findAttribute("name2"); //ano2
String name3 = (String)pageContext.findAttribute("name3"); //ano3
String name4 = (String)pageContext.findAttribute("name4"); //ano4
pageContext.forward("/pageDemo02.jsp");
%>
<%--使用EL表达式输出${}--%>
<h2>取出的值为:</h2>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
</body>
</html>
pageDemo02.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String name1 = (String)pageContext.findAttribute("name1");
String name2 = (String)pageContext.findAttribute("name2");
String name3 = (String)pageContext.findAttribute("name3");
String name4 = (String)pageContext.findAttribute("name4");
%>
<%--使用EL表达式输出${}--%>
<h2>取出的值为:</h2>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
</body>
</html>
- 测试结果(注意对比观察):
8.6 EL表达式、 JSP标签、JSTL标签
8.6.1 EL表达式
<!--JSTL表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
${}
- 获取数据
- 执行运输
- 获取web开发的常用对象
8.6.2 JSP标签
jsptag.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>1</h1>
<%--<jsp:include page=""--%>
<%--
http://localhost:8080/jsptag.jsp?name=trouble&age=26
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="trouble"/>
<jsp:param name="age" value="26"/>
</jsp:forward>
</body>
</html>
jsptag2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>2</h1>
<%--取出参数--%>
Name: <%=request.getParameter("name")%>
Age: <%=request.getParameter("age")%>
</body>
</html>
测试结果:
8.6.3 JSTL标签
-
JSTL标签库的使用就是为了弥补HTML标签的不足;
-
它定义了许多标签,可以供我们使用,标签的功能和Java代码一样。
-
包括核心标签、格式化标签、SQL标签、XML标签
-
引入核心标签库的语法:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 掌握核心标签的部分即可:
- JSTL标签使用步骤如下:
- 引入对应的taglib;
- 使用其中的方法。
- 注意:使用Tomcat 10会报错
org.apache.taglibs.standard.tlv.JstlCoreTLV cannot be cast to jakarta.servlet.jsp.tagext.TagLibraryV
,解决方案:换成Tomcat 9即可
coreif.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入JSPL核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h4>if测试</h4>
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,也登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员,欢迎您"></c:out>
</c:if>
<c:out value="${isAdmin}"></c:out>
</body>
</html>
测试结果:
corewhen.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--定义一个变量为score,该变量的值为88--%>
<c:set var="score" value="88"></c:set>
<c:choose>
<c:when test="${score>=90}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为良好
</c:when>
<c:when test="${score>=70}">
你的成绩为一般
</c:when>
<c:when test="${score<60}">
你的成绩为不及格
</c:when>
</c:choose>
</body>
</html>
测试结果:
coreforeach.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"zhangsan");
people.add(1,"lisi");
people.add(2,"wangwu");
people.add(3,"zhaoliu");
people.add(4,"tianqi");
request.setAttribute("list",people);
%>
<%--
var:每一次遍历出来的变量
items:要遍历的对象
begin:开始位置
end:结束位置
step:步长
--%>
<c:forEach var="person" items="${list}">
<c:out value="${person}"></c:out><br>
</c:forEach>
<hr>
<c:forEach var="person" items="${list}" begin="1" end="4" step="2">
<c:out value="${person}"></c:out><br>
</c:forEach>
</body>
</html>
测试结果: