JSP是简化Servlet编写的一种技术,它将Java代码和HTML语句混合在同一个文件中编写,只对网页中要动态产生的内容采用Java代码来编写。而对固定不变的静态内容采用普通静态HTML页面方式编写。
目录
一、JSP运行原理
JSP本质上是一个Servlet,即与Servlet中相关的方法都可以在JSP中调用。工作原理如下图:
如下为一个jsp文件转换为.java文件后的程序。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>JSP</h1>
<p>你好。。。。。。。。。。</p>
<form action="b.jsp" method="post">
姓名:<input type="text" name="name" />
<input type="submit" value="提交"/>
</form>
</body>
</html>
转换后的Java文件路径,以eclipse为例:工程\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\JspProject\org\apache\jsp 转化结果部分如下:
public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
//省略一部分代码
//这些是JSP的内置对象
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");
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
//JSP中的HTML代码利用IO的方式发送给客户端,所以客户端可以加载出HTML页面
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta charset=\"UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\t<h1>JSP</h1>\r\n");
out.write("\t<p>你好。。。。。。。。。。</p>\r\n");
out.write("\t<form action=\"b.jsp\" method=\"post\">\r\n");
out.write("\t\t姓名:<input type=\"text\" name=\"name\" />\r\n");
out.write("\t\t<input type=\"submit\" value=\"提交\"/>\r\n");
out.write("\t</form>\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
}
二、JSP中的隐含对象
1.九个隐含对象
从上面JSP文件转换后的.java文件我们可以很轻易的得知,其中HTML代码是用IO的方式发送给客户端的,不仅如此我们还可以发现该方法中暴露出来的几个对象,对应到JSP文件中也叫JSP的隐含对象,其实隐含对象一共有九个,如下:
request | HttpServletRequest的对象实例 |
response | HttpServletResponse的对象实例 |
pageContext | 表示页面的上下文,它是javax.servlet.jsp.PageContext的对象实例,可以通过该对象获取其他8个隐含对象,也可以通过它来获取到当前页面的其他信息。 |
session | HttpSession的对象实例 |
application | 当前Web应用的全局变量,是ServletContext的对象实例 |
config | ServletConfig的对象实例 |
out | JspWriter的对象实例。 |
page | 表示当前JSP转换后的Servlet对象,但其被声明为Object类型 |
exception | 在声明了page指令的isErrorPage=“true”才可以使用,由于上面并没有声明,所以找不到该对象。 |
注意:通过查看JSP对应的Servlet文件我们可以知道这些隐含对象虽然可以在JSP文件中直接使用,但是这些变量却是Servlet中的_jspService方法的局部变量,所以JSP中的内容都隶属于该方法。
2.域对象
其中pageContext,request,session,application又被称为四大域对象。原因是它们四个都可以存储和获取指定的属性,只是作用范围不同。下面作用范围从小到大排列为:
- pageContext:属性的作用范围仅为当前JSP页面。
- request:属性的作用范围仅限于同一请求。
- session:属性的作用范围仅限于一次会话。
- application:属性的作用范围限于当前web应用,是最大的属性作用范围,只要在一处设置属性,在本项目的其他各处的JSP或Servlet中都可以获取到。
测试同一请求下四大域对象中属性值的变化,如下(浏览器访问attr1.jsp页面,attr1将请求转发至attr2.jsp):
attr1.jsp如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>attr1页面</h1>
<%
pageContext.setAttribute("pageContext", "attr1中设置的pageContext属性值");
request.setAttribute("request", "attr1中设置的request属性值");
session.setAttribute("session", "attr1中设置的session属性值");
application.setAttribute("application", "attr1中设置的application属性值");
//此处进行请求的转发
request.getRequestDispatcher("./attr2.jsp").forward(request, response);
%>
</body>
</html>
attr2.jsp如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>attr2页面</h1>
<p>
pageContext:<%=pageContext.getAttribute("pageContext") %>
</p>
<p>
request:<%=request.getAttribute("request") %>
</p>
<p>
session:<%=session.getAttribute("session") %>
</p>
<p>
application:<%=application.getAttribute("application") %>
</p>
</body>
</html>
结果如下:
三、JSP基本语法
1.注释
<%-- JSP注释信息写在这里 --%>
<!-- HTML注释信息写在这里 -->
<%
//Java代码注释写在这里
/*
或者
写在这里
*/
%>
JSP注释是对浏览器隐藏的,浏览器控制台无法查看JSP注释,但是可以查看HTML注释。
2.JSP脚本片段
JSP脚本片段是指嵌套在<% 和 %> 之中的一条或多条Java代码。多个脚本片段中的代码可以相互访问。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSP脚本片段测试</title>
</head>
<body>
<%
final int count = 10;
for(int index = 0; index < count; index++){
%>
<p>我是第<%=(index + 1) %>行,总共有<%=count %>行</p>
<%
}
%>
</body>
</html>
3.JSP表达式
JSP表达式用于向页面中输出信息,其语法格式如下:
<%= 表达式%>
注意:表达式的最终运算结果将被转换为字符串显示到浏览器页面上。
示例:
<%
Date date = new Date();
%>
<%-- 注意下面两条语句是等价的 --%>
<% out.print(date); %>
<%= date%>
4.JSP声明
JSP声明用于声明变量和方法。JSP声明的语法格式如下:
<%! 此处声明变量和方法 %>
由于JSP文件本质上是Servlet,其需要转换为.java文件,在服务器端执行,所以对于变量的声明注意区别如下代码:
<% String str1 = "123"; %>
<%! String str2 = "123" %>
这两者是不一样的,因为str1变量最终转换为Servlet后其实质是_jspService方法的局部变量,而str2变量却是转换后Servlet类的成员变量,所以str1不可以是static修饰的,而str2可以。当然还可以声明方法,如下:
<%-- 声明JSP文件转化为Servlet之后的对象方法 --%>
<%!
public int method(int count){
return count + 10;
}
%>
5.JSP指令
JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。语法格式如下:
<%@ 指令 属性名 = "属性值" %>
在JSP2.0中,定义了page、include和taglib这三种指令,每种指令中又都定义了一些各自的属性,如果需要在一个JSP页面中设置同一条指令的多个属性,可以使用多条指令单独设置每个属性,也可以使用同一条指令设置该指令的多个属性。
<%@ page ... %> | 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等 |
<%@ include ... %> | 包含其他文件 |
<%@ taglib ... %> | 引入标签库的定义 |
page指令
page指令相关的属性如下:
include指令:静态包含
JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。Include指令的语法格式如下:
<%@ include file="文件相对 url 地址" %>
注意利用上述方式包含的文件被称为静态包含或静态引入。静态包含最终只会转化为同一个Servlet,它是先包含后编译。它就像是将被包含的JSP文件中的代码复制粘贴到目标文件中一样。类似于C语言中include指令引入文件。
test2.jsp如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test2</title>
</head>
<body>
<%!
static final int DEFAULT_NUMBER = 1;
public void test2Method(){
//TODO
}
%>
<%
String str = "test2";
%>
</body>
</html>
test1.jsp中静态包含test2.jsp如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test1</title>
</head>
<body>
<%--这里包含test2.jsp文件,该文件中定义的变量方法都会出现在同一个Servlet中 --%>
<%@ include file="test2.jsp" %>
<%
/*
注意这里由于上面已经静态包含了test2.jsp,在test2.jsp中已经定义了str变量。
由于静态包含只会转化为同一个Servlet,先包含后编译。所以下面不能定义str变量,会编译不通过。
*/
//String str = "123";
//注意此处的str2和test2.jsp中的str两个变量均被定义在同一个Servlet类的_jspService方法中
String str2 = "test1";
%>
</body>
</html>
动态包含
使用动态包含的语法格式如下:
<jsp:include page="test2.jsp">
<%--这里为子标记可以传递参数,然后在test2.jsp中可以通过request.getParameter()获取值--%>
<jsp:param value="XXX" name="XXX"/>
</jsp:include>
区别于静态包含,动态包含会产生两个Servlet文件,因为在两个类中所以变量名可以重复。动态包含会产生两个Servlet文件,主文件和被包含的文件各自对应一个Servlet,在运行主文件时才将被包含JSP文件的运行结果引入进来。
test2.jsp如下:
<body>
<%
String str = "test2";
%>
<p>我是test2中的内容</p>
<%=str %>
</body>
test1.jsp动态包含test2.jsp如下:
<body>
<jsp:include page="test2.jsp"></jsp:include>
<%
String str = "test1";
%>
</body>
那么test1.jsp对应的Servlet中只会引入如下第一行代码,include方法转向test2.jsp页面:
//省略……
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "test2.jsp", out, false);
out.write('\r');
out.write('\n');
out.write(' ');
String str = "test1";
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");
//省略……
访问test1.jsp,浏览器显示如下:
静态包含和动态包含的区别于联系
- 处理时间和方式不同:静态包含的文件在页面转译时就合并到一起了,被包含文件与当前页面组合而成的新页面必须符合JSP的语法和逻辑规则,由于是提前合并编译,所以执行速度快;而动态包含是被包含文件语法和逻辑独立于当前页面,单独被JSP引擎编译,当前页面执行时再将被包含文件的运行结果传送给客户端,由于是执行页面时处理包含文件,所以执行速度慢,但是可以通过param子标记传递参数,比较灵活。
- 适用情况不同:静态导入适合导入不会经常变化的资源例如HTML文件,而动态导入适合一个时常变化的资源例如JSP文件。
- 相同点:二者最终只产生一个HTML页面。
四、JSP中错误页面的处理
在web项目中我们经常会遇到需要显示自定义的错误页面的情形,以下有两种方式。
1.局部处理
在可能发生错误的JSP页面声明page指令的errorPage=“需要转向的错误页面地址”。(error.jsp)
在错误页面error.jsp中如果设置isErrorPage = “true”,就可以使用exception对象打印出错误信息。
2.全局处理
在web.xml中配置。
指定错误代号的方式
<error-page>
<!-- 指定错误代号 -->
<error-code>404</error-code>
<!-- 指定出错了要去的页面地址 -->
<location>/error.jsp</location>
</error-page>
指定错误类型的方式
<error-page>
<!-- 这里可以指定错误类型 -->
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
需要注意的是:如果局部和全局一起配置的话会优先跳转到errorPage指定的页面,如果要使用全局配置必须先删除errorPage。