上一章链接:Request和Response
接触了Servlet后,发现Servlet有一些弊端。Servlet并不适合设置html响应体。
为了显示页面,需要大量的response.getWriter().print(“html”)。而html是静态页面,不能包含动态信息。
为了弥补这两者的缺点,JSP就诞生了。
目录
JSP基本定义
JSP组成
JSP原理
服务器对JSP的处理
JSP指令
JSP指令概述
page指令
include指令
taglib指令
JSP九大内置对象
pageContext
JSP动作标签
后话
JSP(Java Server Pages),是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。
JSP可作为请求发起页面(例如显示表单、超链接),也可作为请求结束页面(例如显示数据)。
对于JSP组成,可以用一个伪公式代表:JSP = HTML + JAVA脚本 + JSP标签(指令)
JSP中有无需创建即可使用的九大内置对象。
三种Java脚本:
<%!...%> // 声明,用来创建类的成员变量和成员方法
<%=...%> // Java表达式,用于输出一条表达式或变量的结果
<%....%> // java代码片段,用于定义0~N条Java语句
用例代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
Insert title here
<%
int a = 10;
%>
<%
out.print(a++);
%>
<%=a %>
<%!
int a = 100;
public void show() {
System.out.println(a);
}
%>
<%
out.print(this.a++);
show();
%>
不停地刷新index.jsp页面,显示结果如下:
第一个<%%>定义的a为局部变量,而在<%! %>中定义的变量a和方法show()都是一个类中的成员变量和成员方法。等同于
public class example {
private int a = 100;
public void show() {
...
};
}
既然jsp产生了,那么jsp和servlet之间就有了明确的分工。也就是说,jsp负责提交数据和显示处理后的数据,而servlet就作为一个加工坊,处理传递的数据。
如果是将JavaWeb项目发送到Tomcat目录下运行的话,在Tomcat根目录下的work里的catalina文件夹下的localhost文件夹中会有一个跟项目同名的文件夹。
如果是配置于当前项目文件运行,则在存放Eclipse配置的所有项目的目录下,有这么一个路径:
.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\Java_web\org\apache\jsp
在这个文件夹里包含了一系列的JSP的“真身”,发现所谓的JSP本质上是一个Java文件。以上面的index.jsp为例,发现其被转换成了index_jsp.java和index_jsp.class文件。打开.java文件,如下所示:
可知,index_jsp继承自HttpJspBase。根据代码上的路径查找HttpJspBase源码发现,
HttpJspBase继承HttpServlet。因此,index_jsp间接的实现了Servlet接口。(对这里不太明白的话请返回第一章)
从上面可以得出一个结论:其实JSP就是一个特殊的Servlet。
那么根据Servlet的生命周期,可以得出JSP的生命周期:
1.当jsp页面首次被访问时,服务器会把jsp编译成java文件,然后再把java编译成.class文件;
2.接着服务器会创建该类对象并且调用其service()方法。
而以后的访问同一个jsp时,直接调用service()方法。
jsp的生命周期和servlet的生命周期极为相似,只有一点不同:jsp生命周期还包含了将jsp文件编译成.class文件(即servlet)。
既然知道了JSP的本质是一个servlet,那么将index.jsp和index_jsp.java分别打开比对,可以得知服务器在背后都做了什么工作。
首先是index.jsp中的一个<%!...%>脚本,为了方便观看,将代码附下:
<%!
int a = 100;
public void show() {
System.out.println(a);
}
%>
这一段在java的体现是:
可知,服务器会将<%! %>中的代码段变成该servlet的成员变量和成员方法。
接着再来看JSP剩下的<%%>、<%=%>以及HTML代码是如何处理的,找到源代码文件中的_jspService()方法,如下所示:
综上所示,当客户端访问jsp页面时,服务器将会把jsp页面编译成.class文件后,创建相对应的servlet实例对象并且调用service()方法,产生HTML格式的输出并将其内嵌于HttpResponse传给服务器,服务器以静态HTML网页形式将HttpResponse返回客户端,客户端接收到HttppResponse处理完毕后,动态生成HTML网页。
JSP指令用来设置整个JSP页面相关的属性。语法格式如下:
<%@指令名 attr1="value1" attr2="value2" %>
其中,attr1和attr2为指令属性,value1和value2是属性值。
JSP中有三大指令:page、include、taglib,其中最为常用的就是page指令。
接下来分别讲解这三大指令。
page指令是最为常用的指令,也是属性最多的指令。page指令中没有必须要写的属性,不写任何属性都可以。并且可以重复同一个指令,如下面所示:
<%@page %>
<%@page language="java"%>
<%@page pageEncoding="utf-8"%>
先讲在page里面中的两个重要的属性:pageEncoding和contentType。
pageEncoding
pageEncoding用于指定当前JSP页面的编码,而这个编码是为了告诉服务器,当前JSP页面使用了什么编码,以此来正确地将jsp编译成java文件。
contentType
contentType表示添加一个响应头,本质上等同于response.setContentType()方法。也就是说:
<%@page contentType="text/html;charset=utf-8"%>
// 等价于
response.setContentType("text/html;charset=utf-8");
如果这两个属性只提供一个,则另一个的默认值为设置的属性的值。也就是说:
// 只设置pageEncoding,不设置contentType
<%@page pageEncoding="utf-8" @%>
// 那么contentType里的charset就是utf-8
// 只设置contentType,不设置pageEncoding
<%@page contentType="text/html;charset=utf-8"%>
// 那么pageEncoding的值就是charset的值
再讲一些补充的指令:
import
如其名,导包作用。不同的包之间可以用逗号隔开或者多次复写。
errorPage和isErrorPage:
errorPage:当前页面如果抛出异常,则转发到某一页面,由errorPage决定。
isErrorPage:表示当前页面是否为处理错误的页面,当属性为true时,会设置状态码为500。并且需要注意的是,这个页面可以使用九大内置对象中的exception。这就是之前说的那个特例。
称其为静态包含指令,与RequestDispatcher的include()方法的功能相似,而后者是动态包含。两者的区别就在于包含的时间点不同。
<%@include%>是在编译成java文件时完成的。即一个JSP包含另一个JSP后,共同生成一个servlet,然后生成一个class。
而RequestDispatcher的include()方法,包含和被包含是两个Servlet,即两个class。仅仅把响应的内容在运行时合并了。
需要注意并且理解的地方就只有这一点,以下举个例子,思考一下可行性:
<%
String pagePath = "redirect.jsp"
%>
<%@include file="<%=pagePath%>" %>
taglib指令用于导入标签库。
其中有两个属性:prefix和uri。prefix指定标签库在本页面的前缀,uri指定标签库的位置。
<%@taglib prefix="pre" uri="/xxx-tags"%>
// 由于定义了prefix属性,就得在使用的标签前加上前缀pre
// 这样的做法是为了避免不同的标签库中含有相同标签名而发生错误的情况
这里暂时性只讲这么多,后续再增加新的东西。
首先得知道哪九个对象是JSP中的内置对象,其实在上面的_jspService()方法中的截图已经有所提到。现在列举如下:
out和response.getWriter()方法有一定的区别(有时间就补一下区别在哪)。
上面大部分的对象都已经讲过,ServletContext对象也说过了是“独一无二,与天同寿”。至于request和response在下一章也详细的讲解了。最关键的一个pageContext还没有讲,可以称其为“顶天立地”。
pageContext是JavaWeb中四大域对象的最后一个,相当于一个JSP页面,这个域是在当前JSP页面和当前JSP页面中使用的标签之间共享数据。
pageContext可以获取其他八个内置对象,如下所示:
public abstract Object getPage();
public abstract ServletRequest getRequest();
public abstract ServletResponse getResponse();
public abstract Exception getException();
public abstract ServletConfig getServletConfig();
public abstract ServletContext getServletContext();
public abstract HttpSession getSession();
而这样就可以让pageContext代理其他域,何为代理其他域,即可以使用这个域对象往其他域对象里存储数据。
// 相比于正常的域对象的getAttribute、setAttribute方法多了一个参数scope
abstract Object getAttribute(String name, int scope);
abstract void setAttribute(String name, String value, int scope);
// 而scope参数分别可以取以下值,分别代表每一个域
public static final int APPLICATION_SCOPE
public static final int PAGE_SCOPE
public static final int REQUEST_SCOPE
public static final int SESSION_SCOPE
也可以全域查找某个属性,即四大域中找一个属性。
public abstract Object findAttribute(String name);
查找过程是从小到大,依次在page域、request域、session域、application域范围查找名称为name的数据,如果找到就停止查找。
这里不那么细致地讲解JSP的动作标签,其就像Java代码一样在服务器端执行。以下有两个标签:
用例代码如下:
// a.jsp
a.jsp
// b.jsp
<%
out.println("b.jsp
");
%>
其实就是a.jsp和b.jsp分别生成自己的java文件并且生成class文件,然后由a_jsp去调用b_jsp的_jspService()方法。
那么这个和<%@include>有什么区别,区别在于静态包含和动态包含。
最后还有一个jsp动作标签:
'''
最后还是决定在这里补充JSP指令部分,不另开一章写了
这一章应该就这样了,后续要修改的话再修改吧
下一章打算讲cookie和session
'''
下一章链接:Cookie和Session