Blueming
EL PSY CONGROO
JSP基础

JSP(Java Server Page)基础

Servlet和JSP的相同点

JSP和Servlet从本质上,是相同的技术.
继承结构如下:

接口源代码

public interface JspPage extends Servlet {

    public void jspInit();

    public void jspDestroy();

}
public interface HttpJspPage extends JspPage {

    public void _jspService(HttpServletRequest request,
                            HttpServletResponse response)
       throws ServletException, IOException;
}

Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。


Servlet和JSP最主要的不同点

Servlet的应用逻辑是在Java文件中,并且完全从视图层中的HTML分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器(Controller)。


JSP的执行流程

Servlet/JSP容器首先将JSP页面转换成一个JSP页面的实现类,这是一个实现了JspPage接口或其子接口HttpJspPage的Java类。JspPage接口是Servlet的子接口,因此每个JSP页面都是一个Servlet。转换成功后,容器会编译Servlet类,之后容器加载和实例化Java字节码,并执行它通常对Servlet所做的生命周期操作。对同一个JSP页面的后续请求,容器会查看这个JSP页面是否被修改过,如果修改过就会重新转换并重新编译并执行。如果没有则执行内存中已经存在的Servlet实例。
例子
JSP页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>

<body>
    <%= new Date()%>
    
    <%!
        int anInt = 20180713;
    %>
    
    <%
        out.print("2018年07月13日");
    %>
</body>
</html>

该JSP页面会被容器编译为以下内容,其本质就是Servlet,简化了开发者对视图的开发,避免硬编码.

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.86
 * Generated at: 2018-07-13 07:43:45 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;

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

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

    // 声明标签内的内容的生成位置
    int anInt = 20180713;

  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 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 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 {
    // JSP 内置对象
    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,
      			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("\n");
      out.write("\n");
      out.write("<html>\n");
      out.write("<head>\n");
      out.write("    <title>Title</title>\n");
      out.write("</head>\n");
      out.write("<body>\n");
      
      // 赋值标签内的内容生成位置
      out.print( new Date());
      out.write('\n');
      out.write('\n');
      out.write('\n');
      out.write('\n');
    
      // 脚本标签内的内容生成位置
      Date date = new Date();
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      out.println(sdf.format(date));

      out.write("\n");
      out.write("</body>\n");
      out.write("</html>\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);
    }
  }
}

JSP的三种原始标签

声明标签

<%!  
    int anInt = 20180713;  
%>

根据上面JSP文件编译的结果,可知在声明标签中所编写的代码,最终是类的成员变量.

脚本标签

<%
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    out.println(sdf.format(date));
%>

根据上面JSP文件编译的结果,可知在脚本标签中所编写的代码,最终会在类的_jspService(..)的方法体中出现

赋值标签

<%= new Date() %>

根据上面JSP文件编译的结果,可知在赋值标签中所编写的代码,最终会成为字符输出流的方法参数.


JSP的常用指令标签

  1. <%@ page %>
    1. language属性 指定当前页面的语言,目前只支持java
    2. import属性:导入包
    3. contentType属性: 设置JSP的响应类型,该属性中的值最终会被填充到response.setContentType(..)方法
    4. pageEncoding属性:当前页面从字符到字节用什么字符集转换.如果响应类型是text/html,并且contentType中没有指定字符集,则会在_jspService(..)方法中的response.setContentType(..)方法中默认使用该参数.
    5. sesssion属性:可选值true/false.如果true,访问当前JSP页面就会创建一个Session;如果false,反之则不创建.
    6. errorPage属性: 当前页面发生异常后跳转到指定的错误页面
    7. isErrorPage属性:只能在errorPage页面使用,可选值true/false.如果true,则当前页面可以获取到exception对象并且输出.
  2. <%@ include %>

JSP的9个内置对象

内置对象不用开发者手动创建,可以直接使用
查看上面JSP编译后的代码中的_jspService方法,参数是request和response,方法体里创建了其余的6个内置对象.由于page指令标签中没有isErrorPage="true",所以exception对象没有创建.

JSPServlet
requestHttpServletRequest
responseHttpServletResponse
applicationServletContext
sessionHttpSession(该对象是否创建取决于page指令标签中的session属性,若true则自动创建,若false,反之)
configServletConfig
outPrintWriter的兄弟类JspWriter
exceptionThrowable(该对象是否创建取决于page指令标签中的isErrorPage属性,若true则创建.若false,反之)
page
pageContext

请求转发和重定向的区别

请求转发重定向
服务器端完成客户端完成
速度快速度慢
一次请求和一次响应的过程两次请求和两次响应的过程
浏览器地址栏不改变浏览器地址栏要改变
可以利用request对象来传递数据不能使用request对象传递数据
只能是在同一个服务器下完成可以在不同的服务器间完成

JSTL和EL表达式

参考该博客 JSTL 表达式与 EL 语言