JSP入门
# 90.JSP入门
JSP,全称Java Server Pages,可以理解为一个页面模板,其中既可以写HTML,又可以写Java代码,在访问这个JSP的时候,服务器会将其转换为HTML文件返回给浏览器,用来简化书写
# 在JSP出现之前
一个页面,通常是包含动态内容和静态内容。例如一个页面上,有展示用户名的地方,此时用Servlet的话我们只能这样做:
resp.getWriter().write("<p>您好" + username + "欢迎访问</p>" );
而一个完整的HTML页面,内容是非常多的,我们得一个个write:
resp.getWriter().write("<HTML>");
resp.getWriter().write("<body>");
resp.getWriter().write("<p>您好" + username + "欢迎访问</p>" );
resp.getWriter().write("</body>");
resp.getWriter().write("</HTML>");
2
3
4
5
6
7
在上古时代,通常情况是美工写好html静态页面后,丢给Java程序员。Java程序猿在Servlet中的Service方法里,逐句复制html静态页面上的html语句到Servlet的中,并将需要动态展示的地方动态拼接字符串(例如上面的第4行)。
然后浏览器就会拿到拼接好的HTML文本,并展示到浏览器上,按这种方式,要想拼接数据并完整输出一个html页面,没个几百上千行resp.getWriter.write()
是不可能的。所以基本上敲完两个页面两根手指就麻了。
# 动态生成
而同样是上古时期,PHP和ASP就优秀得多了,不搞这繁琐的一套,它们选择在HTML页面中嵌入相应语言来引入动态数据,避免了手动拷贝HTML片段输出的尴尬局面。例如ASP:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
@foreach(var x in Request)
{<li>@x</li>}
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
上述代码会自动执行,生成一系列的 li标签,省去了我们手工拼接的麻烦!
因为仔细想来,我们的主要目的就是希望在最终输出的html的代码中嵌入后台数据罢了,除了把html语句拿出来在Servlet里拼接好再输出这种方式外,我们也可以直接在html语句中写入动态数据!这两种几乎是完全相反的设计思路!孰优孰劣,一看便知。
Java也不甘落后,因此也发明了JSP
# JSP介绍
JSP全称Java Server Page,直译就是“运行在服务器端的页面”。上面已经介绍过,我们可以直接在JSP文件里写HTML代码,使用上把它当做HTML文件。而且JSP中HTML/CSS/JS等的写法和HTML文件中的写法是一模一样的。但它毕竟不是HTML,而且本质差了十万八千里。因为我们还可以把Java代码内嵌在JSP页面中,很方便地把动态数据渲染成静态页面。这一点,打死HTML都做不到。
当有人请求JSP时,服务器内部会经历一次动态资源(JSP)到静态资源(HTML)的转化,服务器会自动帮我们把JSP中的HTML片段和数据拼接成静态资源响应给浏览器。也就是说JSP是运行在服务器端,但最终发给客户端的都已经是转换好的HTML静态页面(在响应体里)。
即:JSP = HTML + Java片段(各种标签本质上还是Java片段)
# JSP的本质
JSP本质上就是一个Servlet,访问一个JSP,服务器内发生的事情如下:
- 服务器解析浏览器的HTTP请求,寻找是否有JSP文件,没有则返回404
- 有该文件,则会将JSP文件转为一个Servlet(一个.java文件)
- 编译该Servlet会被编译为字节码,然后生成Servlet对象,处理请求并返回给浏览器
我们来亲自验证下。首先在web目录下新建index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>Hello JSP!</h1>
</body>
</html>
2
3
4
5
6
7
8
9
启动Tomcat,并访问http://localhost:8080/hello/index.jsp。
我们可以看到工作空间目录为:
Using CATALINA_BASE: "C:\Users\peterjxl\AppData\Local\JetBrains\IntelliJIdea2022.3\tomcat\f48a6060-bf20-4710-adb8-4ae1308d09c7"
我们打开这个目录,可以看到有work目录。之前我们学习Tomcat的时候,讲解过目录结构,work目录是存放运行时产生的一些临时文件。我们直接进入到最里面的文件夹:
C:\Users\peterjxl\AppData\Local\JetBrains\IntelliJIdea2022.3\tomcat\f48a6060-bf20-4710-adb8-4ae1308d09c7\work\Catalina\localhost\hello\org\apache\jsp
可以看到有一个Java文件和一个class文件:
我们打开这个Java文件:
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
2
3
我们可以看到其继承了HttpJspBase类,而这个类我们可以在Tomcat的源码中找到:apache-tomcat-9.0.73-src\java\org\apache\jasper\runtime\HttpJspBase.java,打开,可以看到其继承了HttpServlet。
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {
因此,我们验证了一个点:JSP本质上是一个Servlet。index_jsp.java里还干了什么事情呢?既然是个Servlet,肯定有个service方法,我们继续往下看,可以看到这样的代码:
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
//... 省略其他代码
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" $END$\n");
out.write(" </body>\n");
out.write("</html>\n");
//... 省略其他代码
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
破案了,原理Servlet本质上也是拼接字符串来输出HTML文件的!因此使用JSP,能极大简化我们的代码。
# JSP的基本书写
既然JSP里可以写Java代码,要怎么写呢?不可能说我直接写个Java代码,Tomcat就会知道要执行。例如我们有一个User对象,要展示其用户名:
<h1>user.getName()</h1>
上述代码,肯定是不会被当成Java代码来执行的。
要如何与HTML标签区分出来呢?有如下3种格式:
-
<% 代码 %>
:定义的Java代码,编译后在Servlet的service方法中。service方法中可以写什么,该脚本中就可以写什么。 -
<%! 代码 %>
:定义的Java代码,在JSP转换后会放在类的成员位置。相当于定义一个成员变量、成员方法,用的较少,因为之前讲过,最好不要定义,怕引发线程安全问题 -
<%= 代码 %>
:相当于定义了一个输出语句,会将代码里的执行结果输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。
我们来演示下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>Hello JSP!</h1>
<% System.out.println("Hello"); %>
<%! int i = 1; %>
<%= "hello".toUpperCase() %>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
更新Tomcat,访问http://localhost:8080/hello/index.jsp。
首先可以看到IDEA里打印了Hello,这是第8行的代码起作用了;
我们还是访问JSP编译后的index_jsp.java,可以看到有我们定义的变量 i ;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
int i = 1;
2
3
4
5
最后我们可以看到页面下面有HELLO,这是第12行代码的作用