JSP 概述

JSP(全称为 JavaServer Pages)是由 Sun 公司主导建立的一种动态网页技术标准。JSP 部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成 HTML、XML 或其他格式文档的 Web 网页,然后返回给请求者。JSP 技术以 Java 语言作为脚本语言,为用户的 HTTP 请求提供服务,并能与服务器上的其它 Java 程序共同处理复杂的业务需求。

JSP 适合用来处理动态网页的生成,其实本质上还是会翻译成正规的 Servlet 的 Java 程序,只不过 JSP 的技术让我们开发动态网页更加方便了。

JSP 原理

JSP 本质上是一个 Servlet 程序,当我们启动 Tomcat 的时候,会将我们写的 JSP 程序翻译成一个完整的 Servlet 程序,在:\JetBrains\IntelliJIdea2020.3\tomcat\d2e3b327-bf64-4ef1-9da7-9edd1aeafdaa\work\Catalina\localhost\BookStore\org\apache\jsp 里面,可以看到 .java.class 文件,这就是被 Tomcat 临时翻译的 Servlet 程序。(因为还没有正式部署到 Tomcat 里面)

JSP 基本语法

JSP 表达式

<%= Expression %> 会翻译为 out.print(Expression) 输出。所以表达式不可以带分号,否则会报错。

比如下面代码使用 JSP 表达式在一级标题标签里面输出时间。

1
<h1><%= new Date() %></h1>

JSP 脚本

<% Codes; %> 里面的多行代码会被完整的平移到 _jspService() 方法里面,成为方法内部执行代码。因为是完整平移,所以可以作脚本片段拼接,实现循环输出 HTML 代码:

1
2
3
4
5
6
7
8
<%
for(int i=1;i<=10;i++) {
%>
<h1><%= new Date() %></h1>
<br/>
<%
}
%>

JSP 声明

<%! Codes; %> 里面的多行代码会被完整的平移到类里面(非方法),所以里面只可以声明属性和方法(或者静态代码块)。上面的 JSP 脚本就不可以声明这些(因为在 _jspService() 方法内部)只可以执行代码,而 JSP 声明相反。

1
2
3
4
5
6
7
8
9
10
<%!
private void toOut() {
for(int i=1;i<=10;i++) {
System.out.println(new Date());
}
}
%>
<%
toOut();
%>

JSP 注释

<%-- JSP 注释 --%> 隐式 JSP 注释,在 Servlet 代码中看不到。

<!-- HTML 注释 --> 显式注释,在客户端可以看到这个 html 注释源码。

<% // /*Java 注释*/ %> 隐式 Java 注释,在 Servlet 代码中看不到。

嵌套

各个 JSP 块之间是无法嵌套的,必须独立使用!!!

_jspService() 提供对象

从生成的 Servlet 代码中可以看到,在 _jspService() 方法中,已经为我们提供了下面的八个实例化好的对象,供我们在 <% %> 代码块中使用。

1
2
3
4
5
6
7
8
9
response.setContentType("text/html;charset=UTF-8");
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;

JSP 指令

JSP 指令是告诉 Servlet 转化引擎一些信息,类似于配置信息,格式为:<%@ 指令 属性名="值" %>,属性可以写在一个指令语句中(用空格分开),也可以写在多个指令语句中。

page 指令

page 指令用于定义 JSP 页面的各种属性,无论 page 指令出现在 JSP 页面中的什么地方,它作用的都是整个 JSP 页面。

  • [ language="java" ] 指明页面的脚本语言,默认为 Java

  • [ extends="package.class" ] 指定生成的 Servlet 程序继承的类,默认为 HttpJspBase,间接继承

  • [ import="{package.class | package.*}, ..." ] 用于导入页面所用的外部类

  • [ session="true | false" ] 指明页面是否使用 session 会话,默认为 true

  • [ buffer="none | 8kb | sizekb" ] 用于设置页面缓冲区,建议使用默认的 8kb

  • [ autoFlush="true | false" ] 设置自动刷新缓冲区默认为 true

  • [ isThreadSafe="true | false" ] 表明页面是否使用了多线程,默认为 true

  • [ info="text" ] 设置一个字符串信息 text,用于描述本页面的信息

  • [ errorPage="relative_url" ] 指定页面出错时跳转页面的位置,设置优先度高于 web.xml

  • [ isErrorPage="true | false" ] 用于标注本页面为错误处理页面,会报错错误信息到 exception 中,默认为 false

  • [ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] 指定页面的数据类型,编码等。

  • [ pageEncoding="characterSet | ISO-8859-1" ] 指定数据编码格式

  • [ isELIgnored="true | false" ] 控制 JSP 某些版本的用法是否被忽略

include 指令

用于引入外部的 jsp 文件,插入到当前的位置,要求两个页面的 page 指令不能冲突。因为引入外部 jsp 页面之后,会将两个文件合在一起,翻译成一个 Servlet 程序。

head.jsp:

1
2
3
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1 style="color:red">这玩意是文件头</h1><br>

foot.jsp:

1
2
3
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1 style="color:pink">这玩意是文件尾</h1>

将上面两个文件引入到当前 jsp 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="Error.jsp" %>
<html>
<head>
<title>Test for include</title>
</head>
<body>
<%@ include file="head.jsp"%>
<h1>俺是文件中间!</h1><br>
<%@ include file="foot.jsp" %>
</body>
</html>

JSP 内置对象

NO. 内置对象 类型
1 pageContext javax.servlet.jsp.PageContext
2 request javax.servlet.http.HttpServletRequest
3 response javax.servlet.http.HttpServletResponse
4 session javax.servlet.http.HttpSession
5 application javax.servlet.ServletContext
6 config javax.servlet.ServletConfig
7 out javax.servlet.jsp.JspWriter
8 page java.lang.Object
9 exception java.lang.Throwable

四大作用域

pageContext,request,session,application,四种对象的作用域范围测验:

1
2
3
4
5
6
7
8
9
10
<%
pageContext.setAttribute("One", "pageContext");
request.setAttribute("One", "request");
session.setAttribute("One","session");
application.setAttribute("One", "application");
%>
pageContext 作用域:<%=pageContext.getAttribute("One") %> <br>
request 作用域:<%=request.getAttribute("One") %> <br>
session 作用域:<%=session.getAttribute("One") %> <br>
application 作用域:<%=application.getAttribute("One") %> <br>

打开上面的页面,发现所有的都有值,说明都在作用域范围内。

1
2
3
4
pageContext 作用域:pageContext
request 作用域:request
session 作用域:session
application 作用域:application
  • pageContext 作用域限制在当前的页面中,下面添加请求转发,离开当前页面,发现 pageContext 属性值无法读取,已经离开作用域:
1
<% request.getRequestDispatcher("/head.jsp").forward(request, response); %>

读取结果:

1
2
3
4
pageContext 作用域:null
request 作用域:request
session 作用域:session
application 作用域:application
  • request 作用域限制在一次请求中,当我们直接访问 /head.jsp 发现因为是新的一次请求,而并非设置 request 的请求 + 转发,所以 request 属性值失效。
1
2
3
4
pageContext 作用域:null
request 作用域:null
session 作用域:session
application 作用域:application
  • session 作用域是一次会话,会话指从打开网页到关闭浏览器,这一个过程被称为一个会话。当我们访问 test.jsp 之后关闭网页,再次访问 /head.jsp 发现脱离上一次会话之后,session 中属性值失效。
1
2
3
4
pageContext 作用域:null
request 作用域:null
session 作用域:null
application 作用域:application
  • application 作用域是整个 BookStore 项目,在这个项目下面,所有的页面何时何地都可以获取 application 设置的属性值,只有访问其他 Blog 之类的项目时,才会脱离 application 的作用域。

out 对象输出

out 有自己的缓冲区,可以使用 page 命令设置,只有这个缓冲区满了或者 out 调用了 flush() 方法,才会将 out 缓冲区内容交给 Writer 缓冲区处理。

所以涉及到 out 的缓冲区问题,我们在 JSP 网页中一般都使用 out 来输出而不是 response.getWriter() 来输出。因为 JSP 的网页的 HTML 等都是使用 out 来输出,我们自己乱用 response.getWrite() 来输出会打乱其中的代码顺序。

JSP 标签

需要注意一下子的是,下面的东西在填写参数的时候,可以使用 JSP 表达式来填写参数 XXX=<%=Expression%>

jsp:include

格式: <jsp:include page="XXX"/> 然后回将对应路径的文件也翻译成 Servlet 程序,然后在本页面使用下面这个函数调用生成的 Servlet 程序,这种包含方式被称为动态包含。

1
JspRunTimeLibrary.include(request,response,"/XXX/XXX.jsp",out,false);

也就是说,对于动态包含是会生成两个 Servlet 程序,而对于静态包含,则会将两个合并为一个文件,然后生成一个 Servlet 程序。

因为在调用的时候会传入 request response 参数,所以可以用其请求作用域来传递数据,很方便很好用。当然,一般在开发的时候都会去使用静态包含而不是动态包含。

jsp:forward

格式 <jsp:forward page="XXX/XXX.xxx" >request.getRequestDispatcher("XXX/XXX.xxx").forward(request, response); 效果相同。

jsp:param

格式:<jsp:param name="ParaName" value="ParaValue"/> 夹在 <jsp:forward></jspforward> 之间,作为转发请求时的附加 Get 参数。其中参数可以是其他的任何引用类型。