JSP基础
JSP简介
JSP,即 Java Server Pages,Java 服务器页面,即运行在服务器端的页面。它是由 Sun 公司倡导,许多国际性大公司参与,一起建立的一种动态网页技术。JSP 技术是在传统的静态网页 HTML文件中插入 Java 代码片断和 JSP 标签后形成的一种文件,其后缀名为.jsp。使用 JSP 开发的 Web应用是跨平台的,既能在 Linux 上运行,也能在其他操作系统上运行。
- 在pom.xml文件中引入SpringBoot内嵌Tomcat对jsp的解析包
<dependencies>
<!--内置tocat对Jsp支持的依赖,用于编译Jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--jstl的支持,c标签-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
- 在application.properties文件的中手动指定jsp最后编译的路径
server-port=8080
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp
- 在controller中写入如下代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
//@ResponseBody
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "index";
}
}
- 在jsp文件中写入如下代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ZMT</title>
</head>
<body>
hello 小桃气
</body>
</html>
- 运行结果
JSP规范
将 JSP 页面翻译为 Servlet 的过程,是由 Tomcat 完成。在 Tomcat 中内置了一个 JSP 翻译引擎,当第一次访问该 JSP 页面时,翻译引擎会将 JSP 页面翻译为Servlet 的.java 文件,再将其编译为.class 文件进行运行。
SUN 公司制定的JavaEE 规范中包含两个很重要的子规范:Servlet 规范,及 JSP 规范。其中JSP规范中就包含了如何将JSP页面翻译为Servlet。例如,JSP页面中的HTML、CSSJavaScript,及普通文本部分,均会被翻译到 out. write ()中
Tomcat 中的 JSP 翻译引擎,就遵循了这个 JSP 规范。
JSP 注释
在 JSP 页面中使用注释,可以将要注释的内容使用<%-- --%>括起来。
当然,在 JSP 页面中也可以使用 HTML 的注释
。 但它们的使用效果是不同的。
- HTML 注释会被 JSP 翻译引擎翻译到 Servlet 的 out.write()中;而 JSP 注释则会被 JSP 翻
译引擎忽略,在 Servlet 中是看不到的。 - 在客户端浏览器查看源码时,HTML 注释是可以查看到的;但 JSP 注释在客户端是查看不到的。
例如,在 JSP 页面中写入如下注释:
<body>
hello 小桃气
<%--这是JSP注释--%>
<!--这是HTML注释-->
</body>
JSP 的 Java 代码块
JSP 的 Java 代码块也称为 JSP 小脚本,是以<% %>括起来的部分,其中可以编写 Java代码。编写在 Java 代码块中的代码,会被 JSP 翻译引擎将其置入到 Servlet 的_jspService()方法中作为方法中的 Java 语句出现。所以,其中存放的必须是以分号结尾的 Java 语句。
需要注意的是,JSP 代码块可以放置在 JSP 页面的任意位置,可以放置任意数量。但它们都会被按照顺序放置到 Servlet 的_jspService()方法中。也就是说,其执行顺序是与其在 JSP页面中的出现顺序是一致的。例如
被翻译到 Servlet 的_jspService()方法中为:
由于 JSP 小脚本是被翻译到了 Servlet 的_jspService()方法中,所以 JSP 小脚本中是不能出现如下内容的:
(1)声明的变量是不能添加权限访问控制符的
(2)不能定义方法
(3)不能定义静态语句块
JSP 的声明语句块
在 JSP 页面中使用<%! %>括起来的部分,称为声明语句块。声明语句块中的内容,将被 JSP 引擎翻译到 Servlet 的类体中,没有包含到哪个方法体中。
这样的话,在 JSP 的声明语句块中就可声明实例变量、实例方法、静态方法、静态代码块等内容。并且,这些内容均可被 JSP 的 Java 代码块中代码访问。因为它们原本就是一个Servlet 类中的代码。不过,还是不建议在 JSP 的声明语句块中声明实例变量。因为 JSP 是运行在单例多线程环境下的,实例变量将会引起线程安全问题。
需要注意的是,在 JSP 的声明语句块中,是不能编写普通的 Java 语句的。否则的话,这些语句将会直接出现在 Servlet 的类体中。
声明语句块也可以定义在 JSP 页面的任意位置,且可以定义任意多个。
在 JSP 的 Java 代码块与声明语句块中,均可使用 Java 的单行注释与多行注释。该注释将会出现在 Servlet 中的相应位置。
JSP 的表达式块
在 JSP 页面中使用<%= %>括起来的部分,称为表达式块。其可在 JSP 页面中输出变量、常量,及它们组成的各种表达式的值。注意,是表达式,而不是语句,是没有分号的。该表达式将被 JSP 引擎翻译到_jspService()方法的 out.write()方法中输出。例如
被 JSP 引擎翻译为 Servlet 后为:
JSP 核心
内置对象
在 JSP 的 Java 代码块、表达式块等中可以直接使用的引用,称为 JSP 的内置对象。常用的内置对象有九个,分别是:
这九个对象在 JSP 的 Java 代码块、表达式块中可以直接使用。只所以可以直接使用,是因为 Java 代码块与表达式块被 JSP 引擎翻译后均出现在了 Servlet 的_jspService()方法中。而这九个对象,就是_jspService()方法中的局部变量。在 JSP 的 Java 代码块、表达式块中的代码就是_jspService()方法中的代码,所以在其中可以直接使用。
pageContext
pageContext,页面上下文,其具有一个只在当前页面范围的域属性空间,即其具有setAttribute()方法与 getAttribute()方法。但,由于在当前页面范围,数据不存放到域属性空间也可直接使用,将数据存放到域属性空间反而感觉“多此一举”,所以这些方法并不常用。
不过,在同一页面中,为了使用 EL 表达式(后面学习)来访问某变量的值,此时一般使用pageContext。pageContext 具有一些 get 方法,可以获取到 Request、Response、Session、ServletContext、ServletConfig、page(即当前 Servlet)、exception、out 等另外八个内置对象。
但由于这些对象本身就是内置对象,在 JSP 页面中可以直接使用,所以这些方法也并不常用。不过,在后面将要学习的 EL 表达式中,将会经常使用 pageContext 的这些方法。
application
application,即 ServletContext。所以 ServletContext 所具有的方法,application 都具有。常用的方法例如,
- String getInitParameter ():
获 取 web.xml 文 件 的 中 指 定 名 称 的 上 下 文 参 数 值 。 例 如getInitParameter(“myDBDriver”);会返回字符串“com.mysql.jdbc.Driver”。 - Enumeration getInitParameterNames():
获取 web.xml 文件的中的所有的上下文参数名称。其返回值为枚举类型 Enumeration。 - void setAttribute(String name, Object object):
在 ServletContext 的公共数据空间中,也称为域属性空间,放入数据。这些数据对于 Web应用来说,是全局性的,与整个应用的生命周期相同。当然,放入其中的数据是有名称的,通过名称来访问该数据。 - Object getAttribute(String name):
从 ServletContext 的域属性空间中获取指定名称的数据。 - void removeAttribute(String name):
从 ServletContext 的域属性空间中删除指定名称的数据。 - String getRealPath(String path):
获取当前 Web 应用中指定文件或目录在本地文件系统中的路径,是基于盘符的路径。 - String getContextPath():
获取当前应用在 Web 容器中的名称。
out
out,类型为 javax.servlet.jsp.JspWriter。查看 JavaEE 文档,发现 JspWriter 类继承自 IO流的 Writer 类。即 out 就是一个输出流对象。
page
查看 JSP 翻译为的 Servlet,可以看到 page 对象即 Servlet 对象本身。这个对象在实际应用中并不常用。
exception
在普通的 JSP 页面中是不能使用 exception 内置对象的。因为打开 JSP 翻译为的 Servlet,发现其中并没有 exception 对象。若要在页面中直接使用 exception 对象,则需要配合着 page指令使用。
其它对象
其它对象,还有 request、response、session,及 config。它们的用法与之前 Servlet 学习时的用法相同。只不过是直接使用在了 JSP 页面中了
JSP 指令(directive)
JSP 指令的作用是为当前页面做一些基本的属性设置,为当前的页面的运行提供基本的环境。
根据功能的不同,JSP 中包含三类指令:page 指令,即页面指令;include 指令,即包含指令;及 taglib 指令,即标签库指令。无论哪种指令,其使用的语法格式均为如下形式:
page 指令
page 指令用于设置当前 JSP 页面的相关信息。一个 JSP 文件中可以包含多个 page 指令。常用的 page 指令的属性意义及用法如下:
(1) pageEncoding 属性
pageEncoding 属性用于设置当前 JSP 页面所使用的字符编码格式。即,用户在浏览器中通过右击查看编码所看到的编码格式。
(2) contentType 属性
contentType 属性用于设置当前 JSP 页面呈现于用户浏览器中的内容类型,通常为”text/html”类型,即 html 格式的文本。若在 JSP 页面中设置如下
(3) import 属性
import 属性,用于完成在 JSP 页面中导入指定的类。其被 JSP 引擎翻译为 Servlet 中的import 语句。例如,
在 Servlet 中被翻译为了:
若要导入多个类,则在 import 属性值中可使用逗号将这些类分隔。
在 Servlet 中将被翻译为多个 import 语句:
(4) errorPage 属性
errorPage 属性用于指定,当前页面运行过程中发生异常时所要跳转到的页面。
需要注意的是,该属性会被翻译到 Servlet 的_jspService()方法中,即这里出现的路径是一个后台路径,而非前台路径。所以这里指定的发生异常后所要跳转的页面,需要使用以斜杠开头的后台路径。
(5) isErrorPage 属性
若一个页面中指定了发生异常后所要跳转的页面,将会出现一个问题:异常信息被隐藏了。在控制台看不到异常信息,在所跳转的页面中也看不到异常信息。这对于程序员来说,不是件好事,没有足够的信息提示。
此时,一般是希望当异常发生后,在所要跳转的页面中能够给出相应的异常信息。而异常信息是封装在异常对象 exception 中的。也就是说,我们需要在所要跳转的页面中能够获取到异常对象。此时,就需要将这个所要跳转的页面指定为“错误处理页面”。
当一个页面的 page 指令中设置 isErrorPage 的值为 true 时,表明当前页面为一个“错误处理页面”。默认 isErrorPage 的值为 false
(6) session 属性
session 属性用于指定当前页面中是否可以直接使用内置对象 session。默认为true,可以使用。查看 JSP 翻译为的 Servlet,可以看到 session 的创建,使用的是无参方法 getSession()。该方法的功能是,若当前具有 session,则使用当前的 session;若当前没有 session,则会新建一个 session。即 session 对象肯定不为 null.
include 指令
include 指令,即包含指令,用于将指定的文件包含到当前的 JSP 文件中。该指令只有一个属性 file,用于指定要包含的文件。
(1) 用法
被 include 指定包含的文件,可以是 JSP 动态页面文件,也可以是 HTML 静态页面文件。这里定义一个名为 left.jsp 的动态文件。其中定义了一个变量 sum
再定义一个 index.jsp,不仅将 left.jsp 文件包含了进来,还访问了变量 sum。
运行结果为:
(2) 静态联编
查看 Tomcat 的 work 目录中相关子目录,发现只生成了一个 index_jsp.java 的 Servlet 源文件,并没有生成 left_jsp.java 文件。那是因为 JSP 翻译引擎在翻译时,会将 include 指令所指定的文件内容直接翻译到当前 JSP 对应的 Servlet 中,形成一个.java 文件。这就说明一个问题:这个包含操作是在编译之前完成的,是在编译之前由 JSP 翻译引擎完成的,不是在程序运行期完成的。这种包含是一种静态包含,称为静态联编。
由于在编译期就将这些文件合并为了一个 Servlet 文件,所以,整个过程就一个_jspService()方法。也就是说,这些文件之间是可以相互访问局部变的。只要满足变量声明与使用的先后顺序即可
JSP 动作(Action)
在 JSP 页面中大量使用 Java 代码块、表达式块等内容,会使 JSP 页面看起来“杂乱无章”。为了使 JSP 页面看得简洁明了,为了简化 Java 代码,一般情况下,我们会尽量少的使用 Java代码块与表达式块。取而代之的则是使用 EL 表达式、JSTL 标签(后面学习),及 JSP 动作。
JSP 动作是指,使用系统定义好的标签来完成本应由 Java 代码来完成的功能。
JSP 动作的语法格式为:
或:
JSP 动作很多,但在实际开发时常用的就两个:转发动作与包含动作。
这两份个动作的完成,底层使用的是 RequestDispatcher 的 forward()与 include()方法实现的。而这两份种请求转发方式的本质区别是,标准输出流的开启时间不同。forward()方式的标准输出流是在目标资源中开启的标准输出流,而 include()方式的标准输出流则是在当前发出包含运作的页面中开启的。所以,forward()动作的发起页面中是无法向标准输出流中写入数据的;而 include()动作的发起页面及目标页面中均可向标准输出流中写入数据。
这两份个动作都具有一个 page 属性,用于指定要转向的页面。
forward 动作
页面中一旦具有了 forward 动作,那么当前页面中的所有要显示的内容都将无法显示。因为页面直接转发到了下一个页面。
定义 index.jsp 页面
定义 next.jsp 页面
include 动作
include 动作用于完成将指定页面包含到当前页面中的功能。
EL 表达式
获取数据
(1) 从四大域中依次查找数据
EL 只能从 pageConext、request、session、application 四大域属性空间中获取数据。以下方式是无法获取到指定数据的。因为这个数据没有存放在四大域属性空间中。
此时,可以将这个值存放到四大域属性空间中的任意一个中。然后访问其存放时的 key即可
无论是将变量存放到了哪一个域属性空间,通过${key}的方式,均可访问到该数据的值。EL 到底是从哪个域空间中查找指定的 key 的呢?其底层实际是从最小范围依次查找,直到查找到最大范围 application。这期间,只要查找到了,则直接获取,后面的域空间将不再查找。若最终没有查找到,则什么也不输出。
(2) 从指定域中获取数据
从 pageContext 依次查找到 application 域空间,会降低执行效率。若某属性确定存放在某个域属性空间,则可指定直接从该空间中查找。此时需要借助 EL 的四个域属性空间相关的内置对象。
(3) 访问 Bean 的属性
EL 可以通过 ${key.属性}
的方式获取到指定对象的指定属性值。其底层实际调用的是该对象的相应属性的 get 方法。当然,也可以使用${key[‘属性’]或${key[“属性”]
的方式获取。该方式不常用。
(4) 获取数组中的元素
EL 可以通过 ${key[索引]}
的方式获取到指定索引的元素。不过,需要注意的是,若数组中不存在该指定索引的元素,系统并不会抛出数组越界异常
(5) 获取 List 中的元素
与获取数组中的元素相同,通过 ${key[索引]}
的方式可以获取 List 中指定索引的元素。若 List 中不存在该指定索引的元素,系统并不会抛出越界异常
(6) 获取 Map 中的元素
EL 通过 ${attributeName.mapKey}
的方式可以获取指定 Map 的指定 key 的值。
EL
- EL 不能出现在 Java 代码块、表达式块等 JSP 的动态代码部分。
- EL 只能从 pageConext、request、session、application 四大域属性空间中获取数据。
- EL 不会抛出空指针异常。若访问一个 null 对象的属性,则什么也不显示。
- EL 不会抛出数组访问越界异常。若访问一个数组中的不存在的元素,则什么也不显示。
- EL 不具有对字符串进行处理的能力,就连简单的字符串拼接都不行。