Java Web 从入门到退坑 第八章 jsp


Java Web 从入门到退坑 —— 第八章 jsp


By -gregPerlinLi-


1. 什么是 jsp,它有什么用?

​ jsp 的全称是 Java server pages,也就是 Java 服务器页面。

​ jsp 的主要作用是代替 Servlet 程序回传 HTML 页面的数据。

​ 因为 Servlet 程序回传 HTML 页面数据是一件非常繁琐的事情,开发成本和维护成本极高。

​ 示例代码:

<%--
  Created by IntelliJ IDEA.
  User: gregperlinli
  Date: 2021/2/21
  Time: 10:20 上午
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    This is jsp page data!
</body>
</html>

​ jsp 的小结

​ 1. 如何创建 jsp 页面:在 webapp 目录下点击 New > JSP/JSPX 然后填写文件名并敲击回车即可创建。

​ 2. jsp 如何访问:jsp 页面和 HTML 页面一样,都是存放在 webapp 目录下,访问也和 HTML 一致。

2. jsp 的本质是什么

​ jsp 页面本质上是一个 Servlet 程序

​ 当第一次访问 jsp 页面时,Tomcat 服务器回把 jsp 页面翻译为一个 Java 源文件,并且对它进行编译成 .class 字节码程序。在打开该 Java 文件后不难发现其实里面的内容是

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/9.0.43
 * Generated at: 2021-02-21 02:35:23 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.*;

public final class a_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports &#123;

  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 &#123;
    _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;
  &#125;

  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() &#123;
    return _jspx_dependants;
  &#125;

  public java.util.Set<java.lang.String> getPackageImports() &#123;
    return _jspx_imports_packages;
  &#125;

  public java.util.Set<java.lang.String> getClassImports() &#123;
    return _jspx_imports_classes;
  &#125;

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() &#123;
    if (_el_expressionfactory == null) &#123;
      synchronized (this) &#123;
        if (_el_expressionfactory == null) &#123;
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        &#125;
      &#125;
    &#125;
    return _el_expressionfactory;
  &#125;

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() &#123;
    if (_jsp_instancemanager == null) &#123;
      synchronized (this) &#123;
        if (_jsp_instancemanager == null) &#123;
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        &#125;
      &#125;
    &#125;
    return _jsp_instancemanager;
  &#125;

  public void _jspInit() &#123;
  &#125;

  public void _jspDestroy() &#123;
  &#125;

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException &#123;

    if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) &#123;
      final java.lang.String _jspx_method = request.getMethod();
      if ("OPTIONS".equals(_jspx_method)) &#123;
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        return;
      &#125;
      if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) &#123;
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
        return;
      &#125;
    &#125;

    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 &#123;
      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>A</title>\n");
      out.write("</head>\n");
      out.write("<body>\n");
      out.write("    a.jsp page\n");
      out.write("</body>\n");
      out.write("</html>\n");
    &#125; catch (java.lang.Throwable t) &#123;
      if (!(t instanceof javax.servlet.jsp.SkipPageException))&#123;
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try &#123;
            if (response.isCommitted()) &#123;
              out.flush();
            &#125; else &#123;
              out.clearBuffer();
            &#125;
          &#125; catch (java.io.IOException e) &#123;&#125;
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      &#125;
    &#125; finally &#123;
      _jspxFactory.releasePageContext(_jspx_page_context);
    &#125;
  &#125;
&#125;

​ 在跟踪源代码后发现,HttpJspBase 类,它直接继承了 HttpServlet 类,也就是说 jsp 翻译出来的 Java 类间接的继承了 HttpServlet 类,也就是说翻译出来的就是一个 Servlet 程序

​ 总结: 通过翻译的 Java 源代码,可以得知 jsp 就是 Servlet 程序。

​ 通过观察可以发现翻译出来的 Servlet 源代码中的底层实现也是通过输出流把 HTML 页面回传到客户端

3. jsp 的三种语法

3.1. jsp 头部的 page 命令

​ jsp 的 page 指令可以修改 jsp 页面中的一些重要属性或者行为

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

​ 1. language 属性 表示 jsp 翻译后时什么语言,暂时只支持 Java

​ 2. contentType 属性 表示 jsp 返回的数据类型是什么,也是源码中 response.setContentType() 参数值

​ 3. pageEncoding 属性 表示当前 jsp 页面本身的字符集

​ 4. import 属性 和 Java 源代码中的一致,用于导包,导类


​ 以下两个属性是给 out 输出流使用

​ 5. autoFlush 属性 设置当 out 输出缓冲区满了之后是否自动刷新缓冲区(默认值时 true)

​ 6. buffer 属性 设置缓冲区的大小(默认是 8KB)


​ 7. errorPage 属性 设置当 jsp 页面运行时出错自动跳转去的的错误页面路径(路径以 \ 斜杠打头)

​ 8. isErrorPage 属性 设置当前 jsp 页面是否是错误页面(默认是 false)如果是 true 则可以获取错误信息

​ 9. session 属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象(默认是 true)

​ 10. extends 属性 设置 jsp 翻译出来的 Java 类默认继承谁

3.2. jsp 中常用脚本

3.2.1. 声明脚本(很少使用)

​ 声明脚本的格式是:

<%!
    // Declare java code
%>

​ 作用:

​ 可以给 jsp 翻译出来的 Java 类定义属性和方法甚至是静态代码块,内部类等。

​ 示例代码:

<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8"
         errorPage="/error500.jsp"
         language="java" %>
<html>
<head>
    <title>B</title>
</head>
<body>
    b.jsp page
    <!-- 1. Declare class properties -->
    <%!
        private int id;
        private String name;
        private static Map<String, Object> map;
    %>
    <!-- 2. Declare static blocks -->
    <%!
        static &#123;
            map = new HashMap<String, Object>();
            map.put("key1", "value1");
            map.put("key2", "value2");
            map.put("key3", "value3");
            map.put("key4", "value4");
            map.put("key5", "value5");
        &#125;
    %>
    <!-- 3. Declare class methods -->
    <%!
        public int abc() &#123;
            return 12;
        &#125;
    %>
    <!-- 4. Declare inner classes -->
    <%!
        public static class A &#123;
            private int id = 12;
            private String abc = "abc";
        &#125;
    %>
</body>
</html>

​ 翻译出来的 Java 代码:

......
public final class b_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports &#123;
        private int id;
        private String name;
        private static Map<String, Object> map;
        static &#123;
            map = new HashMap<String, Object>();
            map.put("key1", "value1");
            map.put("key2", "value2");
            map.put("key3", "value3");
            map.put("key4", "value4");
            map.put("key5", "value5");
        &#125;
        public int abc() &#123;
            return 12;
        &#125;
        public static class A &#123;
            private int id = 12;
            private String abc = "abc";
        &#125;
  ......
&#125;

3.2.2. 表达式脚本(常用)

​ 表达式脚本的格式是:

<%= // Expression %>

​ 作用:

​ 在 jsp 页面上输出数据。

​ 特点:

​ 1. 所有的表达式脚本都会被翻译到 _jspService() 方法中

​ 2. 表达式脚本都会被翻译成为 out.print() 输出到页面上

​ 3. 由于表达式脚本翻译的内容都在 _jspService() 方法中,所以 _JspService() 方法中的所以对象都可以直接使用

​ 4. 表达式脚本中的表达式不能以分号结束

​ 示例代码:

<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8"
         errorPage="/error500.jsp"
         language="java" %>
<html>
<head>
    <title>B</title>
</head>
<body>
    b.jsp page<br/>
    <!-- 1. Output integer -->
    <!=12%><br/>
    <!-- 2. Output floate -->
    <!=12.34%><br/>
    <!-- 3. Output stirng -->
    <!="I'm String"%><br/>
    <!-- 4. Output object -->
    <!=map%><br/>
</body>
</html>

3.2.3. 代码脚本

​ 格式:

<%
        // Java code
%>

​ 作用:

​ 1. 可以在 jsp 页面中,编写自己需要的功能(写的是 Java 语句)

​ 特点:

​ 1. 代码脚本翻译之后都在 _jspService 方法中

​ 2. 代码脚本由于翻译到 _jspService 方法中,所以在 _jspService 方法中的现有对象都可以直接使用

​ 3. 还可以由多个代码脚本块组合完成一个完整的 Java 语句

​ 4. 代码脚本还可以和表达式脚本一起组合使用,在 jsp 页面上输出数据

​ 示例代码:

<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8"
         errorPage="/error500.jsp"
         language="java" %>
<html>
<head>
    <title>B</title>
</head>
<body>
    b.jsp page<br/>
    <!-- 1. Code script -- if statement -->
    <%
        int i = 12;
        if ( i == 12 ) &#123;
            System.out.println("The i is 12!");
        &#125; else &#123;
            System.out.println("The i is not 12!");
        &#125;
    %>
    <!-- 2. Code script -- for loop statement -->
    <%
        for ( int j = 0; j < 10; j++ ) &#123;
            System.out.println(j);
        &#125;
    %>
    <!-- 3. In the translated java file, the code in the _jspService method can be written -->
    <%
        String username = request.getParameter("username");
        System.out.println("The request parameter for the username is: " + username);
    %>
</body>
</html>

3.3. jsp 中的三种注释

​ 1. HTML 注释

<!-- This is HTML notes -->

​ HTML 注释会被翻译到 Java 源代码中,在 _jspService 方法里以 out.writer() 输出到客户端。

​ 2. Java 注释

// This is single line Java notes
/* This is multiline Java notes */

​ Java 注释通常会写在声明或代码块注释中,会被翻译到 Java 源代码中。

​ 3. jsp 注释

<%-- This is jsp notes --%>

​ jsp 注释可以注释掉 jsp 页面中所有的代码。

4. jsp 九大内置对象

​ jsp 中的内置对象是指 Tomcat 翻译 jsp 页面成为 Servlet 源代码后,内部提供的九大对象。

​ request 请求对象

​ response 响应对象

​ pageContext jsp 上下文对象

​ session 会话对象

​ application ServletContext 对象

​ config ServletConfig 对象

​ out jsp 输出流对象

​ page 指向当前 jsp 的对象

​ expection 异常对象

5. jsp 四大域对象

​ 四个域对象分别是:

对象 所属类 有效范围
pageContex PageContextImpl 类 当前 jsp 页面范围内有效
request HttpServletRequest 类 一次请求内有效
session HttpSession 类 一个会话范围内有效(打开浏览器访问服务器直到关闭浏览器)
application servletContext 类 整个 Web 工程范围内都有效(只要 Web 工程不停止,数据都在)

​ 域对象是可以像 Map 一样存储数据的对象,四个域对象功能一样,不同的是它们对数据的存储范围。

​ 虽然四个域对象都可以储存数据,但是在使用上它们是有优先顺序的。

​ 四个域在使用的时候优先顺序分别是它们从小到的的范围的顺序。

​ pageContext >> request >> session >> application

​ 示例代码:

​ scope.jsp

<body>
    <h1>scope.jsp pages</h1>
    <%
        // Data is saved in each of the four fields
        pageContext.setAttribute("key", "pageContext");
        request.setAttribute("key", "request");
        session.setAttribute("key", "session");
        application.setAttribute("key", "application");
    %>
    Is there a value in pageContext: <%=pageContext.getAttribute("key")%> <br/>
    Is there a value in request: <%=request.getAttribute("key")%> <br/>
    Is there a value in session: <%=session.getAttribute("key")%> <br/>
    Is there a value in application: <%=application.getAttribute("key")%> <br/>

    <%
        request.getRequestDispatcher("/scope2.jsp").forward(request, response);
    %>
</body>

​ scope2.jsp

<body>
    <h1>scope2.jsp pages</h1>
    Is there a value in pageContext: <%=pageContext.getAttribute("key")%> <br/>
    Is there a value in request: <%=request.getAttribute("key")%> <br/>
    Is there a value in session: <%=session.getAttribute("key")%> <br/>
    Is there a value in application: <%=application.getAttribute("key")%> <br/>
</body>

6. jsp 中 out 输出和 response.getWriter 输出的区别

​ response 表示响应,我们经常用于设置返回给客户端的内容(输出)

​ out 也是给用户做输出使用的

CodeOfJsp

​ 由于 jsp 翻译之后底层源代码都是使用 out 来进行输出,所以一般情况下在 jsp 页面中统一使用 out 来进行输出,避免打乱页面输出内容的顺序

​ out.write() 只适合输出字符串

​ out.print() 输出任意数据都没有问题(都转化为字符串后调用的 write 输出)

​ 深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print 来进行输出

7. jsp 的常用标签

7.1. jsp 静态包含

JspStaticInclusion

​ 示例说明:

<!--
        This is static include,
        the jsp path to be included in the top of the file attribute
        the first slash in the address indicates http://ip:port/projectName/,
        webapp directory mapped to code
-->
<%@include file="/include/foot.jsp"%>

​ 特点:

​ 1. 静态包含不会翻译被包含的 jsp 页面。

​ 2. 静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。

7.2. jsp 动态包含

​ 示例说明:

<!--
    This is dynamic include,
    the page attribute is the path to the JSP page you want to include,
    the first slash in the address indicates http://ip:port/projectName/,
    webapp directory mapped to code
-->
<jsp:include page="/include/head.jsp"></jsp:include>

​ 动态包含也可以像静态包含一样,把被包含的内容执行输出到包含位置。

​ 特点:

​ 1. 动态包含会把包含的 jsp 页面也翻译成为 Java 代码

​ 2. 动态包含底层代码使用如下代码去调用 jsp 页面执行输出:

JspRuntimeLibrary.include(request, response, "/include/head.jsp", out, false);

​ 动态包含的底层原理:

JspDynamicInclusionPrinciple

​ 3. 动态包含还可以传递参数


        
        

7.3. jsp 标签-转发

​ 示例说明:



8. Listener 监听器

​ 1. Listener 监听器是 JavaWeb 的三大组件之一,JavaWeb 三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。

​ 2. Listener 它是 JavaEE 的规范,就是接口。

​ 3. 监听器的作用是监听某种事物的变化并回调函数反馈给用户(程序)去做一些相应的处理。

8.1. ServletContextListener 监听器

​ ServletContextListener 它可以监听 ServletContext 对象创建和销毁。

​ ServletContext 对象在 Web 工程启动的时候创建,在 Web 工程停止的时候销毁。

​ 监听到创建和销毁后都会分别调用 ServletContextListener 监听器的方法反馈。

​ 两个方法分别是:

public interface ServletContextListener extends EventListener &#123;

    /**
     * Receives notification that the web application initialization
     * process is starting.
     *
     * <p>All ServletContextListeners are notified of context
     * initialization before any filters or servlets in the web
     * application are initialized.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being initialized
     *
     * @implSpec
     * The default implementation takes no action.
     */
    default public void contextInitialized(ServletContextEvent sce) &#123;&#125;

    /**
     * Receives notification that the ServletContext is about to be
     * shut down.
     *
     * <p>All servlets and filters will have been destroyed before any
     * ServletContextListeners are notified of context
     * destruction.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being destroyed
     *
     * @implSpec
     * The default implementation takes no action.
     */
    default public void contextDestroyed(ServletContextEvent sce) &#123;&#125;
&#125;

​ contextInitialized() 在 ServletContext 对象创建之后马上调用,做初始化。

​ contextDestroyed() 在 ServletContext 对象销毁之后马上调用,

​ 如何使用 ServletContextListener 监听器监听 ServletContext 对象

​ 使用步骤如下:

​ 1. 编写一个类去实现 ServletContextListener

​ 2. 实现其两个回调方法

​ 3. 到 web.xml 中去配置监听器

​ 在 web.xml 中配置监听器:

<!-- Configure listeners -->
<listener>
    <listener-class>com.example.listener.MyServletContextListenerImpl</listener-class>
</listener>


文章作者: gregPerlinLi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 gregPerlinLi !
  目录