JSP 的本质是 Servlet,当用户向指定 Servlet 发送请求时,Servlet 利用输出流动态生成 HTML 页面,包括每一个静态的 HTML 标签和所有在 HTML 页面中出现的内容。
由于包括大量的 HTML 标签、大量的静态文本及格式等,导致 Servlet 的开发效率极为低下。所有的表现逻辑,包括布局、色彩及图像等,都必须耦合在 Java 代码中,这的确让人不胜其烦。JSP 的出现弥补了这种不足,JSP 通过在标准的 HTML 页面中嵌入 Java 代码,其静态的部分无须 Java 程序控制,只有那些需要从数据库读取或需要动态生成的页面内容,才使用 Java 脚本控制。
从上面的介绍可以看出,JSP 页面的内容由如下两部分组成。
➢ 静态部分:标准的 HTML 标签、静态的页面内容,这些内容与静态 HTML 页面相同。
➢ 动态部分:受 Java 程序控制的内容,这些内容由 Java 脚本动态生成。
下面是一个最简单的JSP页面代码。
<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %>
<html>
<head>
<title>欢迎</title>
</head>
<body>
欢迎学习 Java Web 知识,现在时间是:
<%out.println(new java.util.Date());%>
</body>
</html>
上面的页面中第 8 行代码放在<%和%>之间,表明这些是 Java 脚本,而不是静态内容,通过这种方式就可以把 Java 代码嵌入 HTML 页面中,这就变成了动态的 JSP 页面。在浏览器中浏览该页面,将看到如下图所示的页面。
上面 JSP 页面必须放在 Web 应用中才有效,所以编写该 JSP 页面之前应该先构建一个Web应用。在后面介绍的内容都必须运行在 Web 应用中,所以也必须先构建 Web 应用。
从表面上看,,,JSP 页面已经不再需要 Java 类,似乎完全脱离了 Java 面向对象的特征。事实上JSP的本质依然是 Servlet(一个特殊的Java类)每个 JSP 页面就是一个 Servlet 实例——JSP 页面由系统编译成 Servlet,Servlet 再负责响应用户请求。也就是说,JSP 其实也是 Servlet 的一种简化,使用 JSP 时,其实还是使用 Servlet,因为 Web 应用中的每个 JSP 页面都会由 Servlet 容器生成对应的 Servlet。对于 Tomcat 而言,JSP 页面生成的 Servlet 放在 work 路径对应的 Web 应用下。
再看如下一个简单的 JSP 页面。
<!-- 表明这是一个 JSP 页面 -->
<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %>
<html>
<head>
<title> 第二个JSP页面 </title>
</head>
<body>
<!-- 下面是 Java 脚本 -->
<%for(int i = 0;i < 7;i++){
out.println("<font size='" + i + "'>");
%>
小吴博客</font>
<br/>
<%}%>
</body>
</html>
当启动 Tomcat 之后,可以在 Tomcat 的 work\Catalina\localhost\jspPrinciple\org\apache\jsp 目录到如下文件(本 Web 应用名为jspPrinciple,上面 JSP 页的名为 test.jsp): test_jsp.java 和 test_jsp.class。这两个文件都是由 Tomcat 生成的,Tomcat 根据 JSP 页面生成对应 Servlet 的 Java 文件和 class文件。
下面是 test_jsp.java 文件的源代码,这是一个特殊的 Java 类,是一个 Servlet 类。
// JSP 页面经过 Tomcat 编译后默认的包
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
// 继承 HttpJspBase 类,该类其实是 Httpservlet 的子类
public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
// 用于响应用户请求的方法
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,
"", true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("<!-- 表明这是一个 JSP 页面 -->\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title> 第二个JSP页面 </title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<!-- 下面是 Java 脚本 -->\r\n");
for(int i = 0;i < 7;i++){
out.println("<font size='" + i + "'>");
out.write("\r\n");
out.write("小吴博客</font>\r\n");
out.write("<br/>\r\n");
}
out.write("\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);
}
}
}
初学者看到上面的 Java 类可能有点难以阅读,其实这就是一个 Servlet 类的源代码,该 Java 类主要包含如下三个方法(去除方法名中的_jsp 前缀,再将首字母小写)。
➢ init():初始化 JSP/Servlet 的方法。
➢ destroy():销毁 JSP/Servlet 之前的方法。
➢ service():对用户请求生成响应的方法。
即使读者暂时不了解上面提供的 Java 代码,也依然不会影响 JSP 页面的编写,因为这都是由 Web 容器负责生成的,后面介绍了编写 Servlet 的知识之后再来看这个 Java 类将十分清晰。浏览该页面可看到如下图所示的页面。
从上图中可以看出,JSP 页面里的 Java 代码不仅仅可以输出动态内容,还可以动态控制页面里的静态内容,例如,从上图中看到将“小吴博客”重复输出了7次。
根据上图所示的执行效果,再次对比 test.jsp 和 test_jsp.java 文件,可得到一个结论:JSP 页面中的所有内容都由 test_jsp.java 文件的页面输出流来生成。下图显示了 JSP 页面的工作原理。
根据上面的 JSP 页面工作原理图,可以得到如下 4 个结论。
➢ JSP 文件必须在 JSP 服务器内运行。
➢ JSP 文件必须生成 Servlet 才能执行。
➢ 每个 JSP 页面的第一个访问者速度很慢,因为必须等待 JSP 编译成 Servlet。
➢ JSP 页面的访问者无须安装任何客户端,甚至不需要可以运行 Java 的运行环境,因为 JSP 页面输送到客户端的是标准 HTML 页面。
JSP 技术的出现,大大提高了 Java 动态网站的开发效率,所以得到了 Java 动态网站开发者的广泛支持。
评论