我现在已经没有那么热衷java技术,我想现在有必要写一下我学习jsp的一些体
会,否则过一段时间可能我已经忘记jsp是啥玩意了,呵呵(当然,这里写的仅仅
一点点)。
本文使用的环境是jdk1.5,Tomcat5.5版本。本文并不进行JSP与其他相关技术的
比较,不进行JSP在J2EE中扮演作用的讨论,只针对jsp技术的一些细节发表自己的
看法,期望起到抛砖引玉的作用。
1.JSP运行原理
当一个web容器接收到以.jsp为扩展名的URL的访问请求时,它将把该访问请求
交给JSP引擎去处理,JSP引擎负责解释和执行JSP页面。对Tomcat而言,这个JSP引
擎其实就是一个Servlet程序:org.apache.jasper.servlet.JspServlet,大家可以
在tomcat目录下面的conf/web.xml里找到这样一些内容:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
......
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
每个JSP页面在第一次被访问时,JSP引擎把这个jsp页面翻译成一个servlet源
程序,接着再把这个servlet源程序编译成servlet的class类文件,然后由web容器
(其实是里面的servlet引擎)像调用普通servlet程序一样的方式来装载和解释执行
这个由JSP页面的翻译成的servlet程序。(servlet源程序在tomcat目录下的work/
Catalina/localhost下面)。
我的一个JSP文件index.jsp翻译成的servlet的源程序示例如下(所有jsp文件翻
译成的结构都基本如此)。
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static java.util.List _jspx_dependants;
public Object 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()语句
......
......
} 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);
}
}
}
2.JSP的基本语法
我就不具体列举了,因为这个大家随便翻一本讲jsp的书都有讲的。我主要讲一些
注意点。
1)在jsp里如果使用java类,那么你的这些类都必须有包的定义。
2)使用预定义的变量:request,response,out,session,application,config,
pageContext和page。注意这些变量是局部变量!大家可以看到jsp翻译成的servlet源
程序,这些变量都是定义在_jspService方法里的。所以如果你编写的代码翻译后不是
在_jspService方法里,那么你就不能使用这些变量。比如,由于JSP声明<%! Field or
Method Definition %>产生_jspService方法之外的代码,因此在声明中不能访问这些
变量。
3)关于JSP指令像<% directive att="val"%>的形式。有page,include,taglib和
tag。关于JSP动作像<jsp:xxx>...</jsp:xxx>的形式,有jsp:include,jsp:useBean和
jsp:invoke。对于JSP指令中的include和JSP动作的jsp:include很容易让人混淆,我
看到一段英文文字说得比较清楚。
If a file included with <%@ include %> changes, its changes will not be
noticed until the page containing the <%@ include %> directive also changes.
Recall from chapter 1 that the JSP engine notices when files are changed and
processes them automatically. However, the container doesn’t keep track of
which pages include <%@ include %> directives. When page A uses <%@
include %> to include page B, page B’s data is simply included in page A
every time it is compiled. Therefore, page A must be changed—and recompiled—
for any changes in B to take effect. By contrast, <jsp:include> notices
changes immediately.
Because <%@ include %> works as if you had inserted the target file using a
text editor, it only works for basic text, JSP fragments, and so on. If your
application also has a Java servlet, you cannot include it with <%@ include %>.
Instead, you need to refer to it with <jsp:include>. ( Java servlets are an
advanced topic more for programmers than page designers; do not be concerned
if you have no experience with them.)
The <%@ include %> directive is, in many cases, more efficient than
<jsp:include>, but it also uses much more disk space when large files are
included. Either way, the differences in efficiency between the two approaches
usually are not too important.
With <jsp:include>, the two pages involved—call them page A and page
B—are two entirely separate pages. They can use the same names for different
variables, or they can use different prefixes for the same tag library.
With <%@ include %>, because page A and page B are essentially merged
before being compiled, there might be clashes between names within the
two pages.
4)关于JSP动作<jsp:useBean>的说明,其实如果用了表达式语言或者structs的话,不
用
写很冗长的的类似的东西。
3.关于预定义变量的out对象的说明
JSP页面要真正的像客户端输出东西,要注意,可以把out对象有个一个缓冲区,翻译成
的
servlet程序由servelt引擎又给他提供一个缓冲区。out对象是pageContext.getOut()获得
的,
是一个JspWriter类型的对象。servlet的缓冲区内容写向客户端是用
ServletResponse.getWriter()
方法得到的PrintWriter对象的方法写入的。一版情况是jsp页面先写入out对象的缓冲区,
out
对象的缓冲区满了后又写入servlet的缓冲区,最后输出到客户端。当然了,out对象的缓冲
区
是可以通过page指令的buffer和autoflush两个属性进行设置的。buffer可以设置out对象缓
冲区
的有无,大小。当buffer设置不为none时,说明out对象有缓冲区,大小默认是8KB,此时如
果
autoFlush设置为true,则out缓冲区满了会自动刷新,写到servlet缓冲区里去。如果
autoFlush
设置为false,则out缓冲区满了会抛出IOException异常。如果buffer设置为none,那么说
明
out对象没有缓冲区,直接写入的是servlet的缓冲区。此时autoFlush只能设置为true。
比如一个简单的例子:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"
buffer="none"%>
<html>
<head>
<title>Test</title>
</head>
<body>
<%
out.println("one<br>");
response.getWriter().println("two<br>");
%>
</body>
</html>
这个jsp页面输出的是
one
two
当把buffer不设置为none时,输出是
two
one
这个道理是很容易理解的。
接下来另一段内容:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="javax.servlet.ServletOutputStream" %>
<html>
<head>
<title>Test</title>
</head>
<body>
<%
ServletOutputStream out2=response.getOutputStream();
out2.println("test<br>");
%>
</body>
</html>
这个是会报错的,因为
<%
ServletOutputStream out2=response.getOutputStream();
out2.println("test<br>");
%>
这段代码前面的内容已经先调用out.write()了。只要调用了这个
out对象,最后会调用ServletResponse.getWriter()方法输出到
客户端的。这和ServletOutputStream out2=response.getOutputStream();
发生了冲突。大家看getOutputStream()的异常抛出说明就很清楚了:
java.lang.IllegalStateException - if the getWriter method has been
called on this response
java.io.IOException - if an input or output exception occurred
这个我深有体会啊,以前用jspSmartUpload做文件的上传下载时就出现
类似问题,原因找了一段时间才搞明白,当然了,现在我用apache的
commons-fileupload做文件上传下载发现更方便。哈哈。
4.关于JSP使用java代码的策略
从简单应用到复杂应用依次是(根据情况选择一个):
1)直接调用Java代码。(代码直接写在jsp页面中)
2)间接调用Java代码。(很多具体代码写在一个java类后jsp再调用)
3)使用bean。
4)使用MVC框架
5)使用JSP表达式语言(一般和bean及MVC组合使用)。
6)使用定制标签。
ps:我只是想到什么就写了一下什么,本来很多东西想写的,比如JSP标签具体
是怎么回事,JSP中乱码问题等,但是发现要写又要码很多字了。。。。