JSP基础

之前已经介绍了 Servlet,现在就要来介绍 JSP 了,那为什么在已经有了 Servlet 的情况下,还要推出 JSP 呢?在下面给出答案吧。

1.为什么还要推出JSP

为什么还会有 JSP 这个技术呢?明明已经有了 Servlet 啊,而且 Servlet 在接收到用户的请求之后,也可以对请求做处理,并给出响应,这就是因为 Servlet 在响应用户时如果是响应 HTML 格式的内容,将会使程序变得很难维护,只能使用下面的方式来返回想要响应的内容:

response.getWriter().print("响应的内容");

很明显,如果像这样使用 Servlet 来响应用户请求的话,那我们编写代码将会很麻烦,前端开发人员也很难对页面进行调整。而 JSP 的出现就是为了解决这个问题,使得在返回 HTML 格式的内容时也变得十分简单,下面具体介绍 JSP 的相关内容。

2.JSP的运行原理

要想说明 JSP 的运行原理,那就必须先使用一下了,首先创建一个动态 Web 项目(Dynamic Web Project),版本还是选择2.5的吧,名字就叫 webDemo 吧,新建项目完成之后就可以新建 JSP 页面了,因为 JSP 页面需要放在 WebContent 目录下,所以可以现在 WebContent 目录下新建一个文件夹 demo,然后再在里面新建一个 JSP 页面,就叫 demo1.jsp 吧,内容如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>First JSP</h1>
</body>
</html>

然后就可以启动服务器了,想要访问我们上面刚新建的 JSP 的胡,就需要在浏览器的地址栏输入:

http://localhost:8080/webDemo/demo/demo1.jsp

这样我们就可以在页面上看到显示的内容了。

那刚才的 JSP 页面到底是如何运行的呢?其实也是蛮简单的,首先当一个 JSP 被访问的时候,这个 JSP 页面就会被翻译成一个 java 的文件,然后 java 文件再被编译为 class 文件,最后 class 文件运行来响应浏览器的请求。比如我们上面的 demo1.jsp 文件,当它被请求的时候,就会被先翻译为 demo1_jsp.java 文件,然后再被编译为 demo1_jsp.class 文件,最后就是这个 class 运行了。其实关于由 JSP 转化来的 javaclass 文件都是可以找到的,就如同我们上面写的 demo1.jsp 页面,因为最后项目都是部署在 Tomcat 服务器里面的,因此在 Tomcat 里面也就可以找到了,首先到 Tomcat 的安装目录,会发现有一个 work 的文件夹,然后再是 Catalina 的文件夹,然后再是 localhost 的文件夹,进去之后就可以发现我们项目的目录了,也就是 webDemo 的文件夹了,因为 demo1.jsp 页面已经被访问过一次了,因此这里会有一个 org 的文件夹,然后再是一个 apache 的文件夹,进去之后就是一个 jsp 的文件夹,因为我们上面的 JSP 页面是放在 demo 文件夹下面的,因此这里的 jsp 的文件夹下面会有一个 demo 的文件夹,里面就存放着我们上面说的 demo1_jsp.javademo1_jsp.class 两个文件了。比较有意思的一点就是当我们打开 demo1_jsp.java 这个文件时,会发现 demo1_jsp 这个类是继承于 org.apache.jasper.runtime.HttpJspBase 这个类的,很明显这个一个 Tomcat 的类了,当我们去 Tomcat 的源码包里面找上面这个类时,会发现它的类声明是这样的:

public abstract class HttpJspBase extends HttpServlet implements HttpJspPage

发现了没,这个类是继承于 HttpServlet 这个类的,这样的话,就可以说其实 JSP 其实也是一个 Servlet

3.JSP的脚本元素

首先看看是什么组成了 JSP 呢?其实就是在 HTML 的代码中加入 Java 代码,然后再加入一些 JSP 自身的一些东西。那如何在 HTML 的代码中加入 Java 代码呢?这个就可以使用 JSP 的脚本元素了。

JSP 的脚本元素可以分为 3 类:

1.声明标签
2.表达式标签
3.程序代码标签

下面分别进行介绍:

3.1 声明标签

JSP 的声明标签的格式是这样的:

<%! 变量或方法声明 %>

需要注意的一点就是要观察我们在 JSP 中写的 Java 代码片段,在 JSP 文件转换为 java 文件的过程中,那些 java 片段转化成了 Java 类中的哪些组成部分。那怎么才能看到转换的过程呢?这里就要用到上面已经说过的 JSP 运行原理了,我们在 Tomcat 服务器的安装目录中是可以找到转化后的 java 文件的,因此就可以得知我们在 JSP 页面中写的 java 代码片段到底转换为了 Java 中的哪些组成部分了。

首先可以在 JSP 中写一个这样的片段:

<%!
    // 变量声明
    int i =3;
%>

当我们启动项目,然后在浏览器中访问 JSP 页面之后,就可以看到由 JSP 文件转换得到的 java 文件了,当然也可以看到我们上面写的 Java 片段对应到的代码了,很明显,声明标签中的 Java 代码片段会转化为 java 文件当中的成员变量或成员方法。

3.2 表达式标签

JSP 中的表达式标签的格式是这样的:

<%= 表达式 %>

和上面的声明标签一样,我们也需要知道表达式标签在转换得到的 java 文件中到底是 Java 类的哪个组成部分,当然方法也还是和上面的一样,就会发现表达式标签中的代码最后会转换成 Java 类响应函数中输出部分的代码。比如在 JSP 中表达式标签代码是这样的:<%= i %>,那在转换后的 Java 文件中的响应函数中就是这样的一条语句了。

out.print( i );

3.3 程序代码标签

JSP 中的程序代码标签的格式是这样的:

<% 程序代码 %>

相应的代码就会是这样的:

<%
    int x = 5;
%>

同样的,程序代码标签的代码会被翻译成哪一部分呢?这个就是会被转化成 java 文件中响应函数里面的局部变量或代码片段。

3.4 三种标签的使用情况

上面一共是介绍了 3 中标签,那它们的使用情况怎么样呢?一般来说表达式标签和程序代码标签会用的多一些,这是为什么呢?就是因为它们转换成 java 文件时被转换到类中的位置不同,因为表达式标签和程序代码标签都是被转换到了方法体中,而声明标签则是直接被转换为类的成员变量,而我们都知道 JSP 其实就是一个 Servlet ,而 Servlet 是线程不安全的,因此成员变量会有线程安全问题。因此声明标签才会不经常使用。

4.MVC开发模式

首先看一下 ServletJSP 的优缺点:

Servlet:

缺点:显示HTML内容非常麻烦

JSP:

优点:显示数据非常方便
缺点:封装和处理数据不是很擅长

从上面可以看出,其实 ServletJSP 都不是很擅长对数据进行封装和处理,那什么对这个比较擅长呢?那就是 Java 类了,也就是 JavaBean 了,因此我们在开发时就可以使用 Servlet 来控制程序的处理逻辑,JavaBean 来对数据进行封装和处理,而 JSP 则是用来进行显示数据。这也就是我们想要说的 MVC 开发模式了,具体的对应关系则是如下:

M:Model    模型层
V:View    视图层
C:Controller    控制层

采用 MVC 开发模式的话,则对于一个请求的处理应该是这样的,首先是 Servlet 接收到用户的请求,然后再使用 JavaBean 对数据进行封装和处理,最后使用 JSP 将需要响应的数据显示到页面中。

5.路径问题

关于路径,我们在开发的时候会经常使用,不管是前端页面的一个链接,还是后台的一次跳转,都和路径有关,因此解决了路径问题,在开发的时候就会游刃有余了。首先还是看一个例子吧。

首先在 WebContent 目录下有一个 demo2.jsp 的页面,如果我们想要访问它的话,那就需要在浏览器中输入下面的地址了:

http://localhost:8080/webDemo/demo2.jsp

而同时还存在一个 Servlet,它的访问路径就是:

http://localhost:8080/webDemo/ServletDemo

那如果想要在这个 JSP 中访问 Servlet ,应该怎么办呢?比如就使用一个 a 标签,那我们应该怎么写路径呢?看了上面的两个路径之后就会发现,其实 demo2.jspServletDemo 是在同一个目录下面的啊,因此我们的 a 标签也就可以写成下面这样的了。

<a href="ServletDemo">访问ServletDemo</a>

因为表示路径的时候,./ 是表示当前路径的,因此上面的 a 标签其实也可以写成这样的:

<a href="./ServletDemo">访问ServletDemo</a>

如果改变一下,demo2.jsp 不是在 WebContent 目录下,而是在 WebContent 下的 demo2 文件夹下,那在 JSP 中写访问 Servlet 的路径应该怎么写呢?首先那 demo2.jsp 的访问路径就会变成这样的了:

http://localhost:8080/webDemo/demo2/demo2.jsp

Servlet 的访问路径是没有变的,因此如果对比 JSPServlet 的访问路径的话,就可以发现 Servlet 应该是在 JSP 文件的上一级目录里面,而表示路径时,../ 是表示上一级目录的,因此在 JSP 中访问 Servleta 标签就可以写成这样的了。

<a href="../ServletDemo">访问ServletDemo</a>

从上面的这两个例子就可以看出,其实我们使用的是相对路径,根据两个文件的访问路径,然后对比出它们的目录层级,就可以写出相对路径了。但同时也可以发现,这样是稍微有点麻烦的,因为需要先对比两个文件的访问路径,才能确定互相访问时应该使用什么路径。下面再看看绝对路径的使用。

还是刚才的例子,如果在 WebContent 目录下的 demo2.jsp 文件想要使用 a 标签访问 ServletDemo 这个 Servlet,那应该怎么写呢?其实就可以直接在访问路径中写上 Servlet 的访问路径,如下:

<a href="http://localhost:8080/webDemo/ServletDemo">访问ServletDemo</a>

而其实 http://localhost:8080 这一段是可以省略的,因此下面这样也是可以的:

<a href="/webDemo/ServletDemo">访问ServletDemo</a>

那要是在 WebContent 下面的 demo2 文件夹中的 demo2.jsp 想访问上面这个 Servlet ,那应该怎么写呢?很显然,和上面的写一样的就可以了,因为都是从根目录开始访问的,这样来看的话,写绝对路径就比写相对路径来的更通用一些,因此写绝对路径会更好一些。

简单总结一下的话,就是路径的话,是分为相对路径和绝对路径的,相对路径是根据比较访问两个文件的绝对路径,然后得到两者的层级关系,这样就得到了相对路径,则绝对路径就是直接从根目录开始访问的路径,因此会更加通用一些。需要注意的一点就是绝对路径还可以分为服务器端路径和客户端路径,客户端路径都是需要带工程名的,而服务器端路径是不需要带工程名的。

6.请求转发和重定向

当我们想实现页面之间的跳转时,请求转发和重定向都可以达到目的,但是两者还是有区别的,下面分别进行说明。

6.1 请求转发

首先看一下请求转发的原理,有一个 Servlet 和一个 JSP ,当用户请求 Servlet 时,如果在 Servlet 中使用请求转发给 JSP,那么请求就会直接发送给 JSP 页面,JSP 页面中的内容也会相应给用户,而这整个过程都是一次请求。

请求转化的实现主要是依靠 HttpServletRequest 对象和 RequestDispatcher 对象,先看代码:

RequestDispatcher dispatcher = request.getRequestDispatcher("/demo3/demo1.jsp");
dispatcher.forward(request, response);

首先 HttpServletRequest 对象可以依靠父类 ServletRequest 中的

public RequestDispatcher getRequestDispatcher(String path)

方法得到 RequestDispatcher 对象,其中传入的 path 参数就是我们想要转发到的文件,第二步就是使用 RequestDispatcher 对象中的

public void forward(ServletRequest request, ServletResponse response)

就可以实现跳转了。

6.2 重定向

下面再说重定向,同样是一个 Servlet 和一个 JSP ,当用户请求 Servlet 时,Servlet 会重定向到 JSP 中,这个过程是怎样的呢?首先用户发送一个请求到 Servlet ,这时候重定向就相当于 Servlet 告诉用户浏览器要重定向到的 JSP 页面的访问地址,然后用户浏览器再一次对 JSP 的访问地址发送请求,最后 JSP 就会响应用户的请求,整个过程其实是两次请求。

重定向的实现主要是依靠 HttpServletResponse 对象的

public void sendRedirect(String location)

方法,其中的 location 则是需要重定向到的 JSP 的路径,需要注意的是这里的路径是需要带工程名,而上面请求转化的路径是不需要带工程名的。因此重定向的代码就如下所示:

response.sendRedirect("/webDemo/demo3/demo1.jsp");

6.3 请求转发和重定向的区别

请求转发和重定向的区别主要有以下几条:

1.请求转发是一次请求一次响应,而重定向是两次请求两次响应;
2.请求转发时浏览器的地址栏是不会变化的,而重定向时会发生变化;
3.请求转发的路径不带工程名,而重定向的路径是需要带工程名的;
4.请求转发只能在本网站内部,而重定向可以到外部网站。

第一点已经在上面进行说明了,需要注意的一点就是当我们想使用 request 作为域对象保存值时,因为 request 作为域对象的作用范围是一次请求范围,因此对于请求转发是有效的,而对于重定向是无效的;第二点的话就是因为请求转发是一次请求而重定向是两次请求,第三点是需要注意的,当进行请求转发时,整个过程都是一次请求,因此是先请求到 Servlet ,再从 Servlet 请求到 JSP,而 Servlet 已经是在服务端了,因此这个从 ServletJSP 的路径应该是服务器端路径了,我们上面路径的部分已经写过,服务器端路径是不需要带工程名的;而当是重定向的时候,其实第二次请求也相当于是用户浏览器对 JSP 的一次请求,很显然就是客户端路径了,因此是需要带工程名的。

7.总结

其实上面都只是 JSP 的一些基本内容,需要关注的是 JSP 的运行原理,以及 JSP 的脚本元素,经典的 MVC 开发模式,经常会遇到的路径问题,然后就是请求转发和重定向了,其实很多东西最重要的是搞清楚它们基本的原理,然后自己心中要有相应的模型,能够很简单地将一些概念解释清楚,这样就会掌握的比较好。

坚持原创技术分享,您的支持将鼓励我继续创作!