Servlet有两个致命缺点:
- 写在Servlet中的所有HTML标签必须包含java字符串中,这使得处理HTTP响应报文的工作十分繁琐;
- 所有的文本和HTML标记是硬编码,导致即使是表现层的微小变化,如改变背景颜色,也需要重新编译.
JavaServer Pages(JSP)解决了上述两个问题.(在html里穿插java代码)
JSP页面本质上也是一个Servlet.JSP页面在JSP容器中运行,一个Servlet容器通常也是JSP容器.例如,Tomcat就是Servlet/JSP容器.
当第一个JSP页面第一次被请求时,Servlet/JSP容器主要做以下两件事:
- 转换JSP页面到JSP页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage接口或子接口javax.servlet.jsp.HttpJspPage的java类.JspPage是javax.servlet.Servlet的子接口,这使得每个JSP页面都是Servelt.该实现类的雷鸣有Servlet/JSP容器生成.如果出现转换错误,则相关信息将被发送到客户端.
- 如果转换成功,Servlet/JSP容器随后编译该Servlet类,并装载和实例化该类,像其他正常Servlet一样执行生命周期.
对于同一个JSP页面的后续请求,Servlet/JSP容器会先检查JSP页面是否被修改过,如果是,则该JSP页面会被重新翻译,编译并执行.如果不是,则执行已经在内存中的JSPServlet.
JSP页面可以包含模板数据和语法元素.例如,"<%“是一个元素,因为它表示JSP页面中的java代码的开始,”%>"也是一个元素,它是java代码块的结束符.
一个JSP页面不同于一个Servlet的另个方面是,前者不需要添加注解或在部署描述符中配置映射URL.在应用程序目录中每个JSP页面可以直接在浏览器中输入路径页面访问.
<%@ page import="java.text.DateFormat" %>
<%@ page import="java.util.Date" %><%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/3/31/031
Time: 0:21
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSPDemo</title>
</head>
<body>
<%
DateFormat dateFormat = DateFormat.getDateInstance();
String date = dateFormat.format(new Date());
out.print(date);
%>
</body>
</html>
注释
JSP注释支持两种:
- <%–内容–%> //JSP注释,该注释不会发送到浏览器端
- <!–内容–>//HTML/XHTML注释,不会被容器处理,会发送到服务器端.
隐式对象
Servlet容器会传递几个对象给它运行的Servlet.例如通过Servlet的service方法拿到HttpServletRequest和HttpServletResponse对象.
在JSP中,可以通过使用隐式对象来访问上述对象.
对象 | 类型 |
---|---|
request | javax.servlet.ServletRequest |
response | javax.servlet.ServletResponse |
out | java.io.Writer |
session | javax.servlet.http.HttpSession |
application | javax.servlet.ServletContext |
config | javax.servlet.ServletConfig |
pageContext | javax.servlet.jsp.PageContext |
page | javax.servlet.jsp.HttpJspPage |
exception | java.lang.Throwable |
以request为例,该隐式对象代表Servlet/jsp容器传递个Servlet服务方法的HttpServletRequest对象。可以将request理解为一个指向HttpServletRequest对象的引用变量。下面的代码示例,从HttpServletRequest对象中返回username参数值。
<%
String userName = request.getParameter("username");
%>
其中pageContext。它提供了有用的上下文信息,并通过其说明方法来访问各种Servlet对象,如getRequest,getResponse,getServletContext,getServletConfig和getSession。当然这些方法并没怎么常用,因为可以直接通过隐式对象来访问这些对象。
<%
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String header = headerNames.nextElement();
out.println(header + ": " + request.getHeader(header) + "<br/>");
}
%>
<hr/>
<%
out.println("Buffer size: "+response.getBufferSize()+"<br/>");
out.println("Session id: " + session.getId()+"<br/>");
out.println("Servlet name: " + config.getServletName() + "<br/>");
out.println("Servlet info: "+application.getServerInfo()+"<br/>");
%>
指令
page指令
可以使用page指令来控制JSP转换器转换当前的JSP页面的某些方面。例如,可以告诉JSP用于转换隐式对象out的缓冲器的大小,内容类型,以及需要导入的Java类型,等等。
page指令语法如下:
<%@ page attribute1 = "value1" attribute2 = "value2" ...%>
@ 和page之间的空格不是必须的,attribute1,attribute2等是page指令的属性。如下是page指令属性的列表:
-
import:定义一个或多个本页面中将被导入和使用的java类型。如:import = “java.util.List”.此外JSP默认导入如下包:java.lang,javax.servlet,javax.servlet.http,javax.servlet.jsp.
-
session : 值为True,本页面加入会话管理,值为False则相反。默认值为True。若当前页面不存在javax.servlet.http.HttpSession实例,则会创建一个。
-
buffer : 以KB为单位,定义隐式对象out的缓冲大小。必须以KB后缀结尾。默认大小为8KB或者更大(取决于JSP容器)。该值可以为none,这意味着没有缓冲,所有数据将直接写入PrintWriter。
-
autoFlush :默认值为True。若值为True,则当输出缓冲满时会自写入输出流。而值为False,则仅当调用隐式对象的flush方法时,才会写入输出流。因此,若缓冲溢出,则会抛出异常。(简而言之,就是是否自动清理缓冲)
-
isThreadSafe : 定义该页面的线程安全级别。不推荐使用JSP参数,因为使用该参数后,会生成一些Servlet容器已过期的代码。
-
info: 返回调用容器生成的Servlet类的getServletInfo方法的结果。
-
errorPage : 定义当出错时用来处理错误的页面。
-
isErrorPage: 标识本页是一个错误处理页面。
-
contentType:定义本页面隐式对象response的内容类型,默认是text/html.
-
pageEncoding:定义本页面的字符编码,默认是ISO-8859-1.
-
isELIgnored:配置是否忽略EL表达式。EL是Expression Language的缩写。
-
language:定义本页面的脚本语言类型,默认是java。
-
extends:定义JSP实现类要继承的父类。这个属性说的使用场景非常罕见。
-
deferredSyntaxAllowedAsLiteral:定义是否解析字符串中出现"#{“符号,默认是False,”#{" 是一个表达式的起始符号。
-
trimDirectiveWhitespaces:定义是否不输出多余的空格、空行,默认是False。
大部分page指令可以出现在页面的任何位置,但当page指令包含contentType或PageEncoding属性时,其必须出现在java代码发送任何内容之前。这是因为内容类型和字符编码必须在发送任何内容前设定。
page指令也可以出现多次,但出现多次的指令属性必须具有相同的值。不过import属性例外,多个包含import属性的page指令的结果是累加的。例如,以下page指令将同时导入java.util.ArrayList 和java.util.Date类型。
<%@ page import="java.util.ArrayList"%>
<%@ page import="java.util.Date" %>
如下写法,效果一样:
<%@page import="java.util.Date,java.util.ArrayList" %>
一个page指令可以同时有多个属性。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
include指令
可以使用include指令将其他文件中的内容包含到当前JSP页面。一个页面中可以有多个include指令。若存在一个内容会在多个不同页面中使用或一个页面不同位置使用的场景,则将该内容模块化到include文件非常有用。
include指令语法如下:
<%@ include file = "url"%>
url为被包含文件的相对路径,若url以一个斜杠(/)开始,则该url为文件在服务器的绝对路径,否则为当前JSP页面的相对路径。
例子:
copyright.jspf文件:
copy;2018 veng
include.jsp页面:
<html>
<head>
<title>Title</title>
</head>
<body>
This si the inlcuded content:<hr/>
<%@include file="copyright.jspf"%>
</body>
</html>
运行结果:
如上实例中,为保证include指令能正常工作,copyright.jspf文件必须同include.jsp位于相同目录。按照惯例,以JSPF为扩展名的文件代表 JSP fragement。虽然JSP fragement现在被称为 JSP segment,但为保证一致性,JSPF后缀名依然被保留。
注意,include指令也可以包含静态HTML文件。
此外,include动作(类型于include指令),会在后面详解。
脚本元素
一个脚本程序是一个java代码块,以<% 符号开始,以%>符号结束。
例:
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String host = request.getHeader("host");
%>
<hr/>
<%
out.print(host);
%>
</body>
</html>
这里有两个脚本程序,需要注意的是定义在一个脚本程序中的变量可以被其后续的脚本程序使用。
表达式
每个表达式都会被JSP容器执行,并使用隐式对象out的打印方法输出结果。表达式以"<%=" 开始,并以"%>"结束。如:
<html>
<head>
<title>Title</title>
</head>
<body>
Today is <%= Calendar.getInstance().getTime()%>
</body>
</html>
声明
可以声明能在JSP页面中使用的变量和方法。声明以"<%!" 开始,并以"%>"结束。
例:
<%!
public String getToday(){
return new java.util.Date().toString();
}
%>
<html>
<head>
<title>Title</title>
</head>
<body>
Today is <%= getToday()%>
</body>
</html>
在JSP页面中一个声明可以出现在任何地方,并且一个页面可以有多个声明。可以使用声明来重写JSP页面,实现类的init和destroy方法。通过声明jspInit方法,重写init方法。通过声明jspDestroy方法,来重写destroy方法。
禁用脚本元素
随着JSP2.0对表达式语言的加强,推荐的实践是:在JSP页面中用EL访问服务器端对象且不写Java代码。因此从JSP2.0起,可以通过在部署描述符中的定义一个scripting-invalid元素,来禁用脚本元素。
动作
动作是第三种类型的语法元素,它们被转换成Java代码来执行操作,如访问一个Java对象或调用方法。本节仅讨论所有JSP容器支持的标准动作。除标准外,还可以创建自定义标签执行某些操作。
useBean
useBean将创建一个关联Java对象的脚本变量。这是早期分离的表示层和业务逻辑的手段。随着其他技术的发展,如自定义标签和表达语言,现在很少使用userBean方式。
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:useBean id="today" class="java.util.Date"></jsp:useBean>
<%=today%>
</body>
</html>
setProperty和getProperty
setPropert动作可对一个Java对象设置属性,而getProperty则会输出Java对象的一个属性。
例:
Employee类:
package Bean;
public class Employee {
private String id;
private String firstName;
private String lastName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
getSetPropertyTest.jsp页面
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:useBean id="employee" class="Bean.Employee"/>
<jsp:setProperty name="employee" property="firstName" value="Veng"/>
First Name:<jsp:getProperty name="employee" property="firstName"/>
</body>
</html>
include
include动作用来动态地引入另一个资源。可以引入另一个JSP页面,也可以引入一个Servlet或一个静态的HTML页面。
例:
jspIncludeTest.jsp页面:
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:include page="index.jsp">
<jsp:param name="text" value="How are you?"/>
</jsp:include>
</body>
</html>
index.jsp页面:
<html>
<head>
<title></title>
</head>
<body>
<%= request.getParameter("text")%>
</body>
</html>
这里,要理解include指令和include动作。对于include指令,资源引入发生在页面的转换时,即当JSP容器将页面转化为生成的Servlet时,而对于include动作,资源引入发生在请求页面时。因此,使用include动作是可以传递参数的,而include指令不支持。
第二个不同的是,include指令对引入的文件扩展名不做特殊要求。但对于include动作,若引入的文件需要以JSP页面处理,则其文件扩展名必须是JSP。若使用.jspf为扩展名,则该页面被当作静态页面。
forward
forward将当前页面转向其他资源。
forward.jsp:
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:forward page="login.jsp">
<jsp:param name="text" value="Please Login"/>
</jsp:forward>
</body>
</html>
login.jsp:
<html>
<head>
<title>Title</title>
</head>
<body>
<%= request.getParameter("text")%>
</body>
</html>
错误处理
JSP提供了很好的错误处理能力。除了在Java代码中可以使用try语句,还可以指定一个特殊页面。当应用页面遇到为捕获的异常时,用户将看到一个精心设计的网页解释发生了什么,而不是一个用户无法理解的错误信息。
请使用page指令的isErrorPage属性(属性值必须为True)来标识一个JSP页面是错误页面。
errorHandler.jsp:
<%@page isErrorPage="true" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<%
out.print(exception.toString());
%>
</body>
</html>
buggy.jsp:
<%@page errorPage="errorHandler.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Integer.parseInt("Throw me");
%>
</body>
</html>