接着上篇文章,我们上篇文章讲到了jsp的基本原理以及和servlet的关系,还介绍了jsp的基本语法部分,本篇文章就继续介绍余下的内容。
- 编译指令Page和include
- 基本的动作指令
- 内置对象
一、两个编译指令
Page指令顾名思义,操作当前页面的。首先我们要知道使用编译指令的格式:<%@ page 属性名="属性值"%>,关于page的属性有哪些,如下图所示:
简单说明一下,language指定的是本页面使用的脚本语言,contentType指定了本页面的类型,有text/html,img/png等。import可以引入指定的包,默认情况下已经为我们引入了java.lang.,javax.servlet.jsp.,javax.servlet.http.*。所有在jsp页面引入的包,都会对应到servlet实例中。session指定当前页面是否支持会话(后面详细介绍),errorPage指定了当前页面如果出现异常调用的页面,如果没有为当前页面指定errorPage页面,就会直接抛出异常信息个浏览器(这是我们所不愿意看到的),isErrorPage指定了当前页面是否是错误页面,如果是,当别的页面出错的时候就会跳转到此页面,默认为false。这么多的属性,我们只要稍微有点印象,实际用的时候再过来参考即可。
我们说jsp页面中是不需要处理任何异常的,甚至是检查类异常都是不需要处理的。我们来看看为什么。从servlet类的源代码中找原因,因为这是本质。
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();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}
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;
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title></title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write("\t\t");
for(int a=0;a<10;a++){
out.write("\n");
out.write("\t\t\t<p>Walker</p>\n");
out.write("\t\t");
}
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
} 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);
}
}
我们就看看这个用来响应用户请求的方法,_jspservice(),不知道大家看到没有,整个输出html页面信息的代码块都被try....catch了,而看到catch捕捉的异常时异常机制的源头Throwable ,所有的异常都是继承自它的。而我们看到捕捉到异常之后所进行的操作是,如果当前对象不为空,就调用该对象的处理方法,处理方法就是判断当前页面是否制定的errorPage页面,如果没有抛异常给浏览器,如果有,跳向错误页面。所以无论你jsp页面会抛出什么异常,在被编译到servlet实例中,已经做了处理了,这就是为什么jsp页面不需要处理异常的原因。
第二个指令是include指令,这是一个用来包含指定页面的内容的编译指令,<%@include file="文件路径"%>,我们来看一个例子:
<html>
<head>
<title></title>
</head>
<body>
<h1>这是当前页面</h1>
<%@include file="convert.jsp"%>
</body>
</html>
<html>
<head>
<title></title>
</head>
<body>
<h1>这是引入页面</h1>
</body>
</html>
运行结果的图片就不贴了,从servlet实例中可以明显看出,这一切都做了什么。
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title></title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write("\t\t<h1>这是当前页面</h1>\n");
out.write("\t\t");
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title></title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write("\t<h1>这是引入页面</h1>\n");
out.write(" </body>\n");
out.write("</html>\n");
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
不知道大家看出来了没有,一个页面里竟然包含了两个html标签和title和body标签,可见,这个编译指令就是在编译的时候将要引入的页面中的全部信息直接插入在指定位置上。
二、基本的工作指令
第一个动作指令是,forward指令,这个指令用来向另外的一个页面进行跳转。用法如下:
<html>
<head>
<title>index.jsp</title>
</head>
<body>
<h1>这是当前页面</h1>
<jsp:forward page="convert.jsp">
/*这是传入参数,暂时可以不用关心*/
<jsp:param name="key" value="v" />
</jsp:forward>
</body>
</html>
<html>
<head>
<title>convert.jsp</title>
</head>
<body>
<h1>这是引入页面</h1>
</body>
</html>
从运行结果上看,至少可以看出两点。第一,地址栏上的地址依然是index页面,也就是说页面并没有跳走,第二,我们看原来index页面的所有信息都被替换成convert页面的内容。由此我们可以推断出,所谓的跳转指令,其实并没有完成跳转的操作,只是将目标页面的所有信息全部替换当前页面,这和我们之前说过的一个编译指令很是类似,他是include,我们应该可以记得,include编译指令是将目标页面中所有内容替换到当前页面的某个位置,也就是替换了当前页面中的部分内容。而可以将跳转指令forward理解为替换了当前页面的所有内容。
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>1</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write("\t\t<h1>这是当前页面</h1>\n");
out.write("\t\t");
if (true) {
_jspx_page_context.forward("convert.jsp" + "?" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("key", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("v", request.getCharacterEncoding()));
return;
}
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
从servlet源代码中,我们可以看到,index中前面的代码正常加载,当加载到我们的跳转指令的时候,它实际上构建以个URL:convert.jsp?key=v,默默的请求了此页面然后将返回的结果显示在当前的页面中,其实就是去拿来目标页面中的内容覆盖当前页面,对于index页面的一些请求参数,不会丢失。(关于请求参数的接收,下文介绍)
第二个动作指令,include指令,用来动态引入外部文件到当前页面,和我们的include编译指令相似,但是也有着显著的区别。先看怎么使用:
<html>
<head>
<title>1</title>
</head>
<body>
<h1>这是当前页面</h1>
<jsp:include page="convert.jsp">
<jsp:param name="a" value="c" />
</jsp:include>
</body>
</html>
<html>
<head>
<title>2</title>
</head>
<body>
<h1>这是引入页面</h1>
</body>
</html>
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>1</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write("\t\t<h1>这是当前页面</h1>\n");
out.write("\t\t\n");
out.write("\t\t");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "convert.jsp" + "?" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("a", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("c", request.getCharacterEncoding()), out, false);
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
我们可以看到,动作指令include实际上不想编译指令include将目标页面的所有信息全部插入到指定位置,动态include指令调用的是一个include方法,动态的将目标页面的body部分的内容拿过来,并且还允许传入参数。可能结果和编译指令include一样,但是内部的实现确是截然不同的,各自有各自的适用场所。
第三个动作指令,param,这是我们之前一直在使用的指令,只是一直没有说明。param指令一般都不会单独使用,通常都是结合jsp:include,jsp:forward等使用。使用的格式如下:
<jsp:include page="convert.jsp">
<jsp:param name="a" value="c" />
</jsp:include>
name指定参数名,value指定参数数值。至于怎么获取,我们在介绍request对象的时候加以说明。request对象就是一个专门用于处理用户请求的对象,所以所有的参数传递都是可以使用这个对象进行接收的。还有一些动作指令牵扯到javabean知识,暂时不说。
三、JSP内置对象
我们打开任意的servlet源代码,可以看到在用于响应用户请求的方法_jspservice();的头部有一些成员变量的定义和初始化。看:
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response){
.....
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;
.......
}
接下来我们会详细介绍这些对象的含义及其具体的操作。由于篇幅限制,未完待续。。