jsp

之前聊过用java处理web请求,处理cookie和session等等,但是唯独没有提及如何返回信息。作为一个web程序,肯定需要使用HTML作为用户界面,这个界面需要由服务端返回。返回信息可以使用HttpResponse中的OutputStream对象来写入数据。但是既要在里面写入HTML,又要写入相应的值,造成程序很难编写,同时HTML代码长了也不容易维护。我们需要一种机制来专门处理返回给浏览器的信息。JSP就是用来专门处理这种需求的。

JSP概述

JSP (Java Server Page):Java 服务端页面。是由 Sun Microsystems 公司倡导和许多公司参与共同创建的一种使软件开发者可以响应客户端请求,而动态生成 HTML、XML 或其他格式文档的Web网页的技术标准。jsp可以很方便的在页面中通过java代码嵌入动态页面

JSP原理分析

下面是一个简单的hello world程序

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<%
out.println("hello world");
%>
</body>
</html>

我们将其部署到tomcat服务器上,启动并访问它之后会在tomcat所在目录的 work\Catalina\localhost\JSPDemo\org\apache\jsp (其中JSPDemo是项目名称), 在这个目录下面可以看到生成了一个index_jsp.java、index_jsp.class

下面是这个jsp生成的部分源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {

static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;


try {
response.setContentType("text/html;charset=UTF-8");

out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>index</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" ");

//这里是java代码的开始
out.println("hello world");
//这里是java代码的结尾

out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
//todo someting
} finally {
//todo someting
}
}
}

我们查询一下HttpJspBase这个类可以得到如下继承关系

1
2
3
4
5
6
7
java.lang.Object
|
+--javax.servlet.GenericServlet
|
+--javax.servlet.http.HttpServlet
|
+--org.apache.jasper.runtime.HttpJspBase

也就是说jsp本质上还是一个Servlet类,当我们第一次访问这个jsp页面时,服务器会根据jsp代码生成一个Servlet类的.java源码文件然后编译。对比jsp代码可以看得出来,在翻译的时候它逐行翻译,将html代码采用out.write进行输出,对应的java代码则原封不动的放在对应的位置。

既然它是一个servlet,那么他的生命周期与相关注意事项就与Servlet相同了。

jsp语法

jsp确实简化了用户界面的编写,但是如果只知道原理,而不知道如何使用它仍然是白瞎,这部分来简单聊聊如何使用它

jsp的代码主要放在3种标签中

  1. <% code %>: 这种格式中的代码,主要放的是要执行的java代码,它们最后会被解析到类的service方法中
  2. <%! code %>: 这种格式中的代码,主要包含的是成员变量的定义,它们最后会被解析到类的成员变量定义中
  3. <%= code %>: 这种格式中的代码,最终会被输出到页面上,会被解析到 out.print中进行输出

下面我们对index.jsp进行改造,做一个简单的统计页面访问量的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<%!
private int totalVisited = 0;
%>

<%
totalVisited++;
%>

<%=
"客户请求次数:" + totalVisited
%>
</body>
</html>

然后再看看生成的java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {


private int totalVisited = 0;

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {

final java.lang.String _jspx_method = request.getMethod();

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;


try {
response.setContentType("text/html;charset=UTF-8");

out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>index</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" ");
out.write("\n");
out.write("\n");
out.write(" ");

totalVisited++;

out.write("\n");
out.write("\n");
out.write(" ");
out.print(
"客户请求次数:" + totalVisited
);
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
}
}

jsp内置对象

我们在写jsp页面时关注的其实是Servlet的service 方法,谈及jsp内置对象的时候主要关注的是service中定义的相关变量,从生成的代码上来看,我们可以使用的是service方法中的输入参数request和response 再加它事先定义好的9个局部变量。它们的含义如下:

  1. HttpServletRequest request: 请求对象,之前在HttpServlet中已经了解了它该如何使用
  2. javax.servlet.jsp.PageContext pageContext: 页面的上下文,提供对JSP页面所有对象以及命名空间的访问。可以用它拿到request、cookie、session等一系列页面中可以访问到的对象
  3. HttpSession session: 当前会话
  4. ServletContext application: servlet上下文,我们可以拿到servlet的相关对象。比如获取当前servlet对象的名称,然后拼接一个路径。这样就不用考虑如何部署的问题
  5. JspWriter out: 输出对象
  6. HttpServletResponse response: HTTP响应对象
  7. Object page: 从定义和初始化值来看,它代表的是当前Servlet对象
  8. ServletConfig config: ServletConfig类的实例,获取当前servlet的配置信息
  9. Except: 当前异常,只有当jsp页面是错误页面是才能使用这个对象。

其他的东西基本上用不上,这里也就不再介绍了。

指令

通过上面的相关知识点,现在已经能写相关的jsp代码了,但是既然本质上是servlet类,那么java其他的操作,比如导入相关库文件怎么办呢?这就需要用到对应的jsp指令。jsp指令放在 <%@ code %>中,jsp指令主要有3大类:

  1. page: 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
  2. include: 包含其他文件,可以利用这个属性事先抽取出页面的公共部分(比如页面的头部导航栏和页脚部分),最后再用include做拼接。
  3. taglib: 引入标签库的定义, 这个在使用jstl 和es表达式等第三方jsp扩展库的时候使用

每条指令可以有多个属性,page 指令的相关属性如下:

属性 含义
contentType 等同于 response.setContentType方法,用于设置响应头的Content-Type属性
pageEncoding 设置jsp页面自身的编码方式
language 定义jsp脚本所使用的语言,目前只支持java 语言
import 导入java包
errorPage 当前页面发生异常后会自动跳转到指定错误页面
isErrorPage 标识当前页面是否是错误页面,错误页面中可以使用exception 对象,用来捕获异常

include 指令的相关属性如下:
|属性|含义|
|:—|:—|
|file| 包含的文件路径|

taglib 的属性如下:
|属性|含义|
|:—|:—|
|prefix|前缀,它们是自定义的,将来要用lib中的标签时用它作为前缀|
|uri|第三方库所在路径|