一, 什么是 JSP?
JSP 全称是 Java Server Pages, 它和 servle 技术一样, 都是 SUN 公司定义的一种用于开发动态 web 资源的技术.
JSP 这门技术的最大的特点在于, 写 jsp 就像在写 html, 但它相比 HTML 而言, HTML 只能为用户提供静态数据, 而 Jsp 技术允许在页面中嵌套 java 代码, 为用户提供动态数据.
二, JSP 原理
2.1,Web 服务器是如何调用并执行一个 jsp 页面的?
浏览器向服务器发请求, 不管访问的是什么资源, 其实都是在访问 Servlet, 所以当访问一个 jsp 页面时, 其实也是在访问一个 Servlet, 服务器在执行 jsp 的时候, 首先把 jsp 翻译成一个 Servlet, 所以我们访问 jsp 时, 其实不是在访问 jsp, 而是在访问 jsp 翻译过后的那个 Servlet, 例如下面的代码:
index.jsp
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <HTML>
- <head>
- <base href="<%=basePath%>" rel="external nofollow">
- <title>
- First Jsp
- </title>
- </head>
- <body>
- <% out.print( "Hello Jsp"); %>
- </body>
- </HTML>
当我们通过浏览器访问 index.jsp 时, 服务器首先将 index.jsp 翻译成一个 index_jsp.class, 在 Tomcat 服务器的 work\Catalina\localhost \ 项目名 \ org\apache\jsp 目录下可以看到 index_jsp.class 的源代码文件 index_jsp.java,index_jsp.java 的代码如下:
- package org.apache.jsp;
- import javax.servlet.*;
- import javax.servlet.http.*;
- import javax.servlet.jsp.*;
- import java.util.*;
- public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
- implements org.apache.jasper.runtime.JspSourceDependent {
- private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
- private static java.util.List _jspx_dependants;
- private javax.el.ExpressionFactory _el_expressionfactory;
- private org.apache.AnnotationProcessor _jsp_annotationprocessor;
- public Object getDependants() {
- return _jspx_dependants;
- }
- public void _jspInit() {
- _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
- _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
- }
- public void _jspDestroy() {
- }
- public void _jspService(HttpServletRequest request, HttpServletResponse response)
- throws java.io.IOException, ServletException {
- 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 {
- 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('\r');
- out.write('\n');
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- out.write("\r\n");
- out.write("\r\n");
- out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
- out.write("<html>\r\n");
- out.write("<head>\r\n");
- out.write("<base href=\"");
- out.print(basePath);
- out.write("\">\r\n");
- out.write("\r\n");
- out.write("<title>First Jsp</title>\r\n");
- out.write("\t\r\n");
- out.write("</head>\r\n");
- out.write("\r\n");
- out.write("<body>\r\n");
- out.write(" ");
- out.print("Hello Jsp");
- out.write("\r\n");
- out.write("</body>\r\n");
- out.write("</html>\r\n");
- } catch (Throwable t) {
- if (!(t instanceof SkipPageException)){
- out = _jspx_out;
- if (out != null && out.getBufferSize() != 0)
- try {
- out.clearBuffer();
- } catch (java.io.IOException e) {
- }
- if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
- }
- } finally {
- _jspxFactory.releasePageContext(_jspx_page_context);
- }
- }
- }
我们可以看到, index_jsp 这个类是继承 org.apache.jasper.runtime.HttpJspBase 这个类的, 通过查看 Tomcat 服务器的源代码, 可以知道在 apache-tomcat-6.0.20-src\java\org\apache\jasper\runtime 目录下存 HttpJspBase 这个类的源代码文件, 如下图所示:
我们可以看看 HttpJsBase 这个类的源代码, 如下所示:
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.jasper.runtime;
- import java.io.IOException;
- import javax.servlet.ServletConfig;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.jsp.HttpJspPage;
- import javax.servlet.jsp.JspFactory;
- import org.apache.jasper.compiler.Localizer;
- /**
- * This is the super class of all JSP-generated servlets.
- *
- * @author Anil K. Vijendran
- */
- public abstract class HttpJspBase
- extends HttpServlet
- implements HttpJspPage
- {
- protected HttpJspBase() {
- }
- public final void init(ServletConfig config)
- throws ServletException
- {
- super.init(config);
- jspInit();
- _jspInit();
- }
- public String getServletInfo() {
- return Localizer.getMessage("jsp.engine.info");
- }
- public final void destroy() {
- jspDestroy();
- _jspDestroy();
- }
- /**
- * Entry point into service.
- */
- public final void service(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException
- {
- _jspService(request, response);
- }
- public void jspInit() {
- }
- public void _jspInit() {
- }
- public void jspDestroy() {
- }
- protected void _jspDestroy() {
- }
- public abstract void _jspService(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException;
- }
HttpJspBase 类是继承 HttpServlet 的, 所以 HttpJspBase 类是一个 Servlet, 而 index_jsp 又是继承 HttpJspBase 类的, 所以 index_jsp 类也是一个 Servlet, 所以当浏览器访问服务器上的 index.jsp 页面时, 其实就是在访问 index_jsp 这个 Servlet,index_jsp 这个 Servlet 使用_jspService 这个方法处理请求.
2.2,Jsp 页面中的 HTML 排版标签是如何被发送到客户端的?
浏览器接收到的这些数据
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <HTML>
- <head>
- <base href="http://localhost:8080/JavaWeb_Jsp_Study_20140603/" rel="external nofollow">
- <title>
- First Jsp
- </title>
- </head>
- <body>
- Hello Jsp
- </body>
- </HTML>
都是在_jspService 方法中使用如下的代码输出给浏览器的:
- out.write('\r');
- out.write('\n');
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- out.write("\r\n");
- out.write("\r\n");
- out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
- out.write("<html>\r\n");
- out.write("<head>\r\n");
- out.write("<base href=\"");
- out.print(basePath);
- out.write("\">\r\n");
- out.write("\r\n");
- out.write("<title>First Jsp</title>\r\n");
- out.write("\t\r\n");
- out.write("</head>\r\n");
- out.write("\r\n");
- out.write("<body>\r\n");
- out.write(" ");
- out.print("Hello Jsp");
- out.write("\r\n");
- out.write("</body>\r\n");
- out.write("</html>\r\n");
在 jsp 中编写的 java 代码和 HTML 代码都会被翻译到_jspService 方法中去, 在 jsp 中编写的 java 代码会原封不动地翻译成 java 代码, 如 <%out.print("Hello Jsp");%> 直接翻译成 out.print("Hello Jsp");, 而 HTML 代码则会翻译成使用 out.write("<html 标签>\r\n"); 的形式输出到浏览器. 在 jsp 页面中编写的 HTML 排版标签都是以 out.write("<html 标签>\r\n"); 的形式输出到浏览器, 浏览器拿到 HTML 代码后才能够解析执行 HTML 代码.
2.3,Jsp 页面中的 java 代码服务器是如何执行的?
在 jsp 中编写的 java 代码会被翻译到_jspService 方法中去, 当执行_jspService 方法处理请求时, 就会执行在 jsp 编写的 java 代码了, 所以 Jsp 页面中的 java 代码服务器是通过调用_jspService 方法处理请求时执行的.
2.4,Web 服务器在调用 jsp 时, 会给 jsp 提供一些什么 java 对象?
查看_jspService 方法可以看到, Web 服务器在调用 jsp 时, 会给 Jsp 提供如下的 8 个 java 对象
- PageContext pageContext;
- HttpSession session;
- ServletContext application;
- ServletConfig config;
- JspWriter out;
- Object page = this;
- HttpServletRequest request,
- HttpServletResponse response
其中 page 对象, request 和 response 已经完成了实例化, 而其它 5 个没有实例化的对象通过下面的方式实例化
- pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
- application = pageContext.getServletContext();
- config = pageContext.getServletConfig();
- session = pageContext.getSession();
- out = pageContext.getOut();
这 8 个 java 对象在 Jsp 页面中是可以直接使用的, 如下所示:
- <%
- session.setAttribute("name", "session 对象");// 使用 session 对象, 设置 session 对象的属性
- out.print(session.getAttribute("name")+"<br/>");// 获取 session 对象的属性
- pageContext.setAttribute("name", "pageContext 对象");// 使用 pageContext 对象, 设置 pageContext 对象的属性
- out.print(pageContext.getAttribute("name")+"<br/>");// 获取 pageContext 对象的属性
- application.setAttribute("name", "application 对象");// 使用 application 对象, 设置 application 对象的属性
- out.print(application.getAttribute("name")+"<br/>");// 获取 application 对象的属性
- out.print("Hello Jsp"+"<br/>");// 使用 out 对象
- out.print("服务器调用 index.jsp 页面时翻译成的类的名字是:"+page.getClass()+"<br/>");// 使用 page 对象
- out.print("处理请求的 Servlet 的名字是:"+config.getServletName()+"<br/>");// 使用 config 对象
- out.print(response.getContentType()+"<br/>");// 使用 response 对象
- out.print(request.getContextPath()+"<br/>");// 使用 request 对象
- %>
运行结果如下:
2.5,Jsp 最佳实践
Jsp 最佳实践就是 jsp 技术在开发中该怎么去用.
不管是 JSP 还是 Servlet, 虽然都可以用于开发动态 Web 资源. 但由于这 2 门技术各自的特点, 在长期的软件实践中, 人们逐渐把 servlet 作为 Web 应用中的控制器组件来使用, 而把 JSP 技术作为数据显示模板来使用. 其原因为, 程序的数据通常要美化后再输出: 让 jsp 既用 java 代码产生动态数据, 又做美化会导致页面难以维护. 让 servlet 既产生数据, 又在里面嵌套 HTML 代码美化数据, 同样也会导致程序可读性差, 难以维护. 因此最好的办法就是根据这两门技术的特点, 让它们各自负责各的, servlet 只负责响应请求产生数据, 并把数据通过转发技术带给 jsp, 数据的显示 jsp 来做.
2.6,Tomcat 服务器的执行流程
第一次执行:
客户端通过电脑连接服务器, 因为是请求是动态的, 所以所有的请求交给 Web 容器来处理
在容器中找到需要执行的 *.jsp 文件
之后 *.jsp 文件通过转换变为 *.java 文件
*.java 文件经过编译后, 形成 *.class 文件
最终服务器要执行形成的 *.class 文件
第二次执行:
因为已经存在了 *.class 文件, 所以不在需要转换和编译的过程
修改后执行:
1. 源文件已经被修改过了, 所以需要重新转换, 重新编译.
来源: https://www.jb51.net/article/191459.htm