JavaWeb之JSP原理

Posted 考拉熊_12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb之JSP原理相关的知识,希望对你有一定的参考价值。

1.为什么需要JSP?

在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。如果使用Servlet程序来输出只有局部内容需要改动的网页,其中所有的静态内容也需要程序员用java程序代码产生,整个Servlet程序的代码将非常臃肿,编写和维护都非常困难。对大量静态内容的美工设计和相关html语句的编写,并不是程序员所要做的工作,程序员对此也不一定在行。网页美工设计和制作人员不懂java编程,更是无法完成这样的工作。为了弥补Servlet的缺陷,SUN公司在Servlet的基础上推出了JSP技术作为解决方案。

2.什么是JSP?

JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。

3.如何在MyEclipse中运行jsp文件?

jsp文件一般放在WebRoot文件夹下,可以在WebRoot目录下新建一个文件用来放jsp文件。例如如下的文件目录:

示例中新建了一个helloWorld.jsp文件,Jsp技术允许在页面中嵌套java代码,规定java代码写在<% %>内部,代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
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%>">
    
    <title>My JSP \'helloWorld.jsp\' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
    <%
    Date date=new Date();
    System.out.println(date);
     %>
  </body>
</html>

.jsp文件放在WebRoot目录下的非WEB-INF文件夹下是不需要在web.xml中进行配置的。右击直接运行就可以。

为了减少风险,可以把jsp文件移到WEB-INF 目录下。基于Servlet的声明,WEB-INF不作为Web应用的公共文档树的一部分。因此,WEB-INF 目录下的资源不是为客户直接服务的。我们仍然可以使用WEB-INF目录下的JSP页面来提供视图给客户,客户却不能直接请求访问JSP。放在WEB-INF 目录下需要在web.xml中进行配置。配置方法与servlet的配置一样,只不过是将<servlet-class></servlet-class>标签改为<jsp-file></jsp-file>,标签内填的内容是“/jsp文件所在的文件夹名/jsp文件名”。配置后运行,右击jsp文件,Run As MyEclipse Server Application。运行后显示结果如下:

4.JSP原理

4.1 web服务器是如何调用并执行一个jsp页面的?

浏览器向服务器发请求,不管访问的是什么资源,其实都是在访问Servlet,所以当访问一个jsp页面时,其实也是在访问一个Servlet,服务器在执行jsp的时候,首先把jsp翻译成一个Servlet,所以我们访问jsp时,其实不是在访问jsp,而是在访问jsp翻译过后的那个Servlet,比如上面的helloWorld.jsp文件,当我们通过浏览器访问helloWorld.jsp时,服务器首先将helloWorld.jsp翻译成一个helloWorld.class,(文件路径可以参考:E:\\MyEclipseWorkSpace\\.metadata\\.me_tcat85\\work\\Catalina\\localhost\\MyWebProject\\org\\apache\\jsp\\jspTest,其中MyEclipseWorkSpace为项目所在的工程目录),helloWorld.class的源代码文件helloWorld.java的代码如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/8.5.9
 * Generated at: 2018-10-12 09:24:04 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp.jspTest;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;

public final class helloWorld_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("java.util");
    _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=ISO-8859-1");
      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>My JSP \'helloWorld.jsp\' starting page</title>\\r\\n");
      out.write("    \\r\\n");
      out.write("\\t<meta http-equiv=\\"pragma\\" content=\\"no-cache\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"cache-control\\" content=\\"no-cache\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"expires\\" content=\\"0\\">    \\r\\n");
      out.write("\\t<meta http-equiv=\\"keywords\\" content=\\"keyword1,keyword2,keyword3\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"description\\" content=\\"This is my page\\">\\r\\n");
      out.write("\\t<!--\\r\\n");
      out.write("\\t<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"styles.css\\">\\r\\n");
      out.write("\\t-->\\r\\n");
      out.write("\\r\\n");
      out.write("  <script>\\"undefined\\"==typeof CODE_LIVE&&(!function(e){var t={nonSecure:\\"51550\\",secure:\\"51555\\"},c={nonSecure:\\"http://\\",secure:\\"https://\\"},r={nonSecure:\\"127.0.0.1\\",secure:\\"gapdebug.local.genuitec.com\\"},n=\\"https:\\"===window.location.protocol?\\"secure\\":\\"nonSecure\\";script=e.createElement(\\"script\\"),script.type=\\"text/javascript\\",script.async=!0,script.src=c[n]+r[n]+\\":\\"+t[n]+\\"/codelive-assets/bundle.js\\",e.getElementsByTagName(\\"head\\")[0].appendChild(script)}(document),CODE_LIVE=!0);</script></head>\\r\\n");
      out.write("  \\r\\n");
      out.write("  <body data-genuitec-lp-enabled=\\"false\\" data-genuitec-file-id=\\"wc1-1\\" data-genuitec-path=\\"/MyWebProject/WebRoot/jspTest/helloWorld.jsp\\">\\r\\n");
      out.write("    ");

    Date date=new Date();
    System.out.println(date);
     
      out.write("\\r\\n");
      out.write("  </body>\\r\\n");
      out.write("</html>\\r\\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);
    }
  }
}

 我们可以看到,helloWorld_jsp这个类是继承 org.apache.jasper.runtime.HttpJspBase这个类的,通过查看Tomcat服务器的源代码,如下所示:

/*
 * 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,而helloWorld_jsp又是继承HttpJspBase类的,所以helloWorld_jsp类也是一个Servlet,所以当浏览器访问服务器上的helloWorld.jsp页面时,其实就是在访问helloWorld.jsp这个Servlet,helloWorld_jsp这个Servlet使用_jspService这个方法处理请求。

也就是说,web容器(Servlet引擎)接收到以.jsp为扩展名的URL的访问请求时,它将该访问请求交给JSP引擎去处理。每个JSP页面在第一次被访问时,JSP引擎将它翻译成一个Servlet源程序,接着再把这个Servlet源程序编译成Servlet的class文件,然后再用web容器(Servlet引擎)像调用普通的Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。

4.2 Jsp页面中的html排版标签是如何被发送到客户端的?

浏览器接受到的这些数据(右击该网页,点击查看网页源码):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="http://localhost:8080/MyWebProject/">
    
    <title>My JSP \'helloWorld.jsp\' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  <script>"undefined"==typeof CODE_LIVE&&(!function(e){var t={nonSecure:"51550",secure:"51555"},c={nonSecure:"http://",secure:"https://"},r={nonSecure:"127.0.0.1",secure:"gapdebug.local.genuitec.com"},n="https:"===window.location.protocol?"secure":"nonSecure";script=e.createElement("script"),script.type="text/javascript",script.async=!0,script.src=c[n]+r[n]+":"+t[n]+"/codelive-assets/bundle.js",e.getElementsByTagName("head")[0].appendChild(script)}(document),CODE_LIVE=!0);</script></head>
  
  <body data-genuitec-lp-enabled="false" data-genuitec-file-id="wc1-1" data-genuitec-path="/MyWebProject/WebRoot/jspTest/helloWorld.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>My JSP \'helloWorld.jsp\' starting page</title>\\r\\n");
      out.write("    \\r\\n");
      out.write("\\t<meta http-equiv=\\"pragma\\" content=\\"no-cache\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"cache-control\\" content=\\"no-cache\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"expires\\" content=\\"0\\">    \\r\\n");
      out.write("\\t<meta http-equiv=\\"keywords\\" content=\\"keyword1,keyword2,keyword3\\">\\r\\n");
      out.write("\\t<meta http-equiv=\\"description\\" content=\\"This is my page\\">\\r\\n");
      out.write("\\t<!--\\r\\n");
      out.write("\\t<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"styles.css\\">\\r\\n");
      out.write("\\t-->\\r\\n");
      out.write("\\r\\n");
      out.write("  <script>\\"undefined\\"==typeof CODE_LIVE&&(!function(e){var t={nonSecure:\\"51550\\",secure:\\"51555\\"},c={nonSecure:\\"http://\\",secure:\\"https://\\"},r={nonSecure:\\"127.0.0.1\\",secure:\\"gapdebug.local.genuitec.com\\"},n=\\"https:\\"===window.location.protocol?\\"secure\\":\\"nonSecure\\";script=e.createElement(\\"script\\"),script.type=\\"text/javascript\\",script.async=!0,script.src=c[n]+r[n]+\\":\\"+t[n]+\\"/codelive-assets/bundle.js\\",e.getElementsByTagName(\\"head\\")[0].appendChild(script)}(document),CODE_LIVE=!0);</script></head>\\r\\n");
      out.write("  \\r\\n");
      out.write("  <body data-genuitec-lp-enabled=\\"false\\" data-genuitec-file-id=\\"wc1-1\\" data-genuitec-path=\\"/MyWebProject/WebRoot/jspTest/helloWorld.jsp\\">\\r\\n");
      out.write("    ");

    Date date=new Date();
    System.out.println(date);
     
      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代码。

 4.3 Jsp页面中的java代码服务器是如何执行的?

在jsp中编写的java代码会被翻译到_jspService方法中去,当执行_jspService方法处理请求时,就会执行在jsp编写的java代码了,所以Jsp页面中的java代码服务器是通过调用_jspService方法处理请求时执行的。

 

wx搜索“程序员考拉”,专注java领域,一个伴你成长的公众号!

 

 

以上是关于JavaWeb之JSP原理的主要内容,如果未能解决你的问题,请参考以下文章

JavaWeb之jsp速通

JavaWeb之会话技术

Java实战之04JavaWeb-04JSPEL表达式JSTL标签库

JavaWeb之JSP

JavaWeb---总结(十四)JSP原理

20160331javaweb之JSP 标签技术