<%@include file=”fileName” %> 与<jsp:include flush="true" page="fileName">
看到很多教程已经面试中都对jsp中的两个文件包含指令比较纠结,这里就特别对这两个指令做一个比较直观的对比。
参数指定:filename不带路径时默认为当前页目录,如果设置目录,则需以应用的根目录设置地址。jsp:include中的参数flush必须为true,因为在jsp1.1中false是一个无效的设定值。
下面示例,先看一下<%@include %>的处理方式:修改MyJsp.jsp,内容如下:
<html>
<body>
This is my first JSP! <br/>
<%
String test ="This is a test value!";
%>
Here is include a jsp file, named include.jsp.<br/>
<%@ include file="include.jsp" %>
</body>
</html>
新建一个include.jsp,内容如下:
<!-- start of include.jsp -->
<html>
<body>
This is the include jsp.<br/>
Here will print the String "test" if it's existing. </br>
test = <%= test%>
</body>
</html>
<!-- end of include.jsp -->
浏览器上刷新MyJsp.jsp页面。我们可以看到,页面输出结果为:
This is my first JSP!
Here is include a jsp file, named include.jsp.
This is the include jsp.
Here will print the String "test" if it's existing.
test = This is a test value!
由结果可以看出,Myjsp.jsp中不仅输出了include.jsp文件中的内容,而且include.jsp中程序代码也共用了MyJsp.jsp中所定义的字符串test。打开work下的源文件目录,我们可以发现,目录下面并没有include_jsp.java文件。打开MyJsp_jsp.java文件:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static java.util.Vector _jspx_dependants;
static {
_jspx_dependants = new java.util.Vector(1);
_jspx_dependants.add("/include.jsp");
}
public java.util.List getDependants() {
return _jspx_dependants;
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html");
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("<html>\r\n");
out.write(" <body>\r\n");
out.write(" This is my first JSP! <br/>\r\n");
out.write(" ");
String test ="This is a test value!";
out.write("\r\n");
out.write(" \r\n");
out.write(" Here is include a jsp file, named include.jsp.<br/>\r\n");
out.write(" ");
out.write("<!-- start of include.jsp -->\r\n");
out.write("<html>\r\n");
out.write(" <body>\r\n");
out.write(" This is the include jsp.<br/>\r\n");
out.write(" Here will print the String \"test\" if it's existing. </br>\r\n");
out.write(" test = ");
out.print( test);
out.write("\r\n");
out.write(" </body>\r\n");
out.write("</html>\r\n");
out.write("<!-- end of include.jsp -->");
out.write("\r\n");
out.write(" </body>\r\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
从java代码中可以看到,include.jsp中的内容被直接插入到MyJsp.jsp中输出,这个操作过程相当于直接把include.jsp的内容复制到MyJsp.jsp的相应位置,因此include.jsp可以和MyJsp.jsp直接共享所有的对象,和在同一个页面中编码一样。这里应该注意的是,include.jsp中的代码是始终包含在MyJsp.jsp中的,即使把<%@ include %>放在MyJsp.jsp的最末一行,在生成的java文件中,include的内容也将包含在一个_jspService()方法中。因此include.jsp中对公共对象的设置都会对Myjsp.jsp产生影响,特别是当include.jsp中调用response.getOutputStream()输出并关闭输出流后,如果不重新打开输出流,将会引起OutputStream is already closed的IO异常。
将MyJsp.jsp中的包含更改成jsp:include:
<html>
<body>
This is my first JSP! <br/>
<%
String test ="This is a test value!";
%>
Here is include a jsp file, named include.jsp.<br/>
<jsp:include flush="true" page="include.jsp" %>
</body>
</html>
再次刷新MyJsp.jsp页面,结果显示如下:
This is my first JSP!
Here is include a jsp file, named include.jsp.
Include.jsp的内容并没有显示出来,查看tomcat控制台我们可以看到,有异常报出:
2009-7-12 18:47:45 org.apache.jasper.compiler.Compiler generateClass 严重: Error compiling file: /D:/Program/Java/Tomcat5/work/Catalina/localhost/test//org/apache/jsp\include_jsp.java [javac] Compiling 1 source file D:\Program\Java\Tomcat5\work\Catalina\localhost\test\org\apache\jsp\include_jsp.java:48: 找不到符号 符号: 变量 test 位置: 类 org.apache.jsp.include_jsp out.print( test); ^ 1 错误
这里提示test变量未定义。打开work下源代码目录,我们可以查找到include_jsp.java文件,但找不到include_jsp.class文件,因此在编译时出现了异常。很显然,用jsp:include时,所包含的jsp文件不能共用调用者的对象,并且本身就会编译成java类。
查看MyJsp_jsp.java文件,我们发现,这里关于include.jsp的调用,只有一句代码
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "include.jsp", out, true);
很明显,jsp:include只调用一个工具方法来显示被包含文件的显示结果。两者的代码互不影响(当然,有三个对象还是传递了过去,你可以在include.jsp中改变这几个对象的属性值),因此include.jsp中的异常并不会影响到MyJsp.jsp文件的正常显示。
介于jsp:include并不能与调用者共享自定义的对象,当调用者需要向被包含页面传入参数时,需要使用<param >标签来传入参数,并在被包含页用request.getParameter()方法来获取。将MyJsp.jsp修改一下:
<html>
<body>
This is my first JSP! <br/>
<%
String test ="This is a test value!";
%>
Here is include a jsp file, named include.jsp.<br/>
<jsp:include flush="true" page="include.jsp">
<jsp:param name="test" value="<%=test %>"></jsp:param>
</jsp:include>
</body>
</html>
include.jsp更改为:
<!-- start of include.jsp -->
<html>
<body>
This is the include jsp.<br/>
Here will print the String "test" if it's existing. </br>
test = <%= request.getParameter("test")%>
</body>
</html>
<!-- end of include.jsp -->
刷新MyJsp.jsp,页面即正常显示了。Html:
This is my first JSP!
Here is include a jsp file, named include.jsp.
This is the include jsp.
Here will print the String "test" if it's existing.
test = This is a test value
综合上面比较可以看出,<%@ include %>包含文件时,相当于直接把被包含的文件的内容插入到包含页里面,因此被包含页的每次更新,就会导致所有包含页的全部重新编译,而被包含页并不会主动被编译,可以称之为jsp编译指令。而<jsp:include>则相当于直接调用被包含页的输出结果,被包含页的任意更新并不会导致包含页重新编译,可以称之为jsp运行时调用。这也引出了一个几乎所有教程中都会提到的一个结论:在包含不会经常修改到页面时,应使用<%@include %>,在包含经常修改到的页面时,应使用jsp:include。这里的修改指的是文件本身的修改,在实际系统应用中,网站的前台页面因样式等原因修改的几率比较大,因此应该使用jsp:include,那些以html静态页和jsp动态页来区分使用哪个include的方式,显然是错误的。