JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"

科技资讯 投稿 12900 0 评论

JSP 的本质原理解析:

JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"

目录
    JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"
    • 每博一文案
    • 1. JSP 概述
    • 2. 第一个 JSP 程序
    • 3. JSP 的本质就是 Servlet
    • 4. JSP 的基础语法
      • 4.1 在 JSP 文件中直接编写文字
      • 4.2 在JSP中编写Java程序 <% %> 与 <%! %>
        • 4.2.1 <% %>
        • 4.2.2 <%! %>
      • 4.3 通过JSP当中的 <%= %>向浏览器前端输入 Java变量
      • 4.4 在JSP 中的专业注释
      • 4.5 JSP基础语法总结:
    • 5. JSP的指令
    • 6. JSP的九大内置对象
    • 7. 使用Servlet + JSP完成oa项目的改造
    • 8. 补充:
    • 9. 总结:
    • 10. 最后:

每博一文案

活明白的人,一生只做好了这两件事:
每个瞬间都充满了选择和承担,就算面前是一座独木桥,也必须选择是前进后退,亦或是留在原地此时此刻你所经历的一切。
这是过往无数个选择后的结果,哪些小的选择汇聚在了一起,最终成了我们今天的时光。
其实,活明白的人一生只做好了两件事看,一是选择,二是承担。常听人争论选择和努力哪个更重要。
其实,努力并不是选择的对比面,而是拥有选择的基本条件。不努力的人往往连选择的资格都没有,努力是为了,
更好的选择。正如马伯庸说:所谓的选择只是努力所赋予人的一种资格。
所有的一夜成名,刹那的焰火,实际是过往今年默默努力埋下的伏笔,因此这里说的选择不是投机取巧的小聪明。
而是积淀后的深思熟虑。常言道:选择不对,努力白费。
李兰娟院士在为了汕头大学的毕业学生送上给予时,表示:不管是在生活还是事业上,有很多外部变化和环境因素是我们
所无法选择的。我们可以选择的是在每一个人生路口要做出怎样的选择,以及如何勇敢的前行。
我们所有的人都在共享一段岁月,却在不同的选择中分道扬镳,因为人生最重要的除了努力还有选择。
当你被生活的疲惫裹挟着,被环境的艰难牵制着,这时你要么被生活牵着鼻子走,要么接收痛苦与打击,抗住一场场暴风雪,主动迎击一地鸡毛的琐碎,你要做的不是抱怨生活的不公,而是迈出步子,夺下生活的主导权,做出选择了。
一种选择就是一种代价,不同的选择造就了不同的人生,人打从生下来就面临着很多种未知的可能。
未来都是一张白纸,任由自己去上色作画。有些人完成地很好,也有人画得一团糟,人生的一万种可能好的,不好的
都是有认真选择好。
每一支画笔,在每次选择时都要坚持原则,便是对自己的人生负责。

                                            —————— 《一禅心灵庙语》

1. JSP 概述

JSP(全称JavaServer Pages),sun公司主导的一种动态网页技术,JSP在服务端运行,可以响应客户端的请求,根据请求内容动态的生成HTML、XML或其他格式文档的Web网页然后返回请求者。在JSP页面可以嵌入Java代码,JSP文件在运行时会被其编译器转换成更原始的Servlet代码,然后再由Java编译器来编译成能快速执行的二进制机器码。

2.特点:

    能以模板化的方式简单、高效地添加动态网页内容。
  1. 可利用JavaBean和标签库技术复用常用的功能代码。
  2. 有良好的工具支持。
  3. 继承了Java语言的相对易用性。
  4. 继承了Java的跨平台优势,实现“一次编写,处处运行”。
  5. 页面中的动(控制变动内容的部分)/静(内容不需变动的部分)区域以分散但又有序的形式组合在一起,方便开发维护。
  6. 可与其它企业级Java技术相互配合。JSP可以只专门负责页面中的数据呈现,实现分层开发。

3.JSP页面组成:

普通的 HTML 标记符。

    JSP 标签,如指令标签、动作标签。
  1. 变量和方法的声明。
  2. Java 程序段。
  3. Java 表达式。

2. 第一个 JSP 程序

这里给一个小的建议,大家在阅读如下,文章时,可以带着一个这样的问题:JSP 是什么 ? 去阅读文章,有助于后面的内容上的阅读理解。

WEB-INF目录之外创建一个 index.jsp文件,然后这个文件中没有任何内容。注意 了:我们对于这个jsp 文件当中并没有编写任何的内容,一个字符,一个标点都可以,就是一个空白的。如下显示的:

http://127.0.0.1:8080/servlet14/index.jsp 我们部署的项目的路径:具体显示如下。

重点: 实际上我们访问的以上的这个:index.jsp,底层执行的是:index_jsp.class 这个java程序。我们可以在我们本地电脑上访问到该生成是 index_jsp.class,index_jsp.java的文件,该生成的对应的.class,.java文件所在的路径是在我们启动 Tomcat 服务器当中提示的,一个 CATALINA_BASE路径下的 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62

index_jsp.java编译生成index_jsp.class文件。访问index.jsp,实际上执行的是index_jsp.class中的方法。

index.jsp 在我本地电脑上生成的 CATALINA_BASE的 路径下的,index_jsp.java,index_jsp.class 的文件。 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62\work\Catalina\localhost\servlet14\org\apache\jsp

如下是我们的一个 index.jsp(空内容) 被翻译为 index_jsp.java 文件的内容如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 03:20:48 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;

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

  private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
          jakarta.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("jakarta.servlet";
    _jspx_imports_packages.add("jakarta.servlet.http";
    _jspx_imports_packages.add("jakarta.servlet.jsp";
    _jspx_imports_classes = null;
  }

  private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
      throws java.io.IOException, jakarta.servlet.ServletException {

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

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html";
      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(' ';
      out.write(' ';
    } catch (java.lang.Throwable t {
      if (!(t instanceof jakarta.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;
    }
  }
}

3. JSP 的本质就是 Servlet

从上述我们编写的第一jsp 程序,index.jsp 空内容的,index.jsp访问的时候,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index_jsp 这就是一个类。

extends org.apache.jasper.runtime.HttpJspBase 类的,我们查阅其 Tomcat 10 的 HttpJspBase 源码如下:

如下是 HttpJspBase 的源码内容

/*
 * 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 jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.HttpJspPage;

import org.apache.jasper.Constants;
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 {

    private static final long serialVersionUID = 1L;

    protected HttpJspBase( {
    }

    @Override
    public final void init(ServletConfig config
        throws ServletException
    {
        super.init(config;
        jspInit(;
        _jspInit(;
    }

    @Override
    public String getServletInfo( {
        return Localizer.getMessage("jsp.engine.info", Constants.SPEC_VERSION;
    }

    @Override
    public final void destroy( {
        jspDestroy(;
        _jspDestroy(;
    }

    /**
     * Entry point into service.
     */
    @Override
    public final void service(HttpServletRequest request, HttpServletResponse response
        throws ServletException, IOException
    {
        _jspService(request, response;
    }

    @Override
    public void jspInit( {
    }

    public void _jspInit( {
    }

    @Override
    public void jspDestroy( {
    }

    protected void _jspDestroy( {
    }

    @Override
    public abstract void _jspService(HttpServletRequest request,
                                     HttpServletResponse response
        throws ServletException, IOException;
}

所以:一个index_jsp类就是一个Servlet类。总结就是:一个 index.jsp 文件会被翻译为一个 index_jsp.java 类,而 该 翻译的 index_jsp.java类是 继承 了 org.apache.jasper.runtime.HttpJspBase 类的,而 org.apache.jasper.runtime.HttpJspBase 类 是继承了 HttpServlet 类的。这下逻辑就清晰了,JSP 就是 Servlet

    JSP 的生命周期和Servlet的生命周期完全相同。完全就是一个东西。没有任何区别。
  • JSP和servlet一样,都是单例的。(假单例,真单例是:其类的构造器是 private 私有化的,而Servlte 的构造器不是 private 私有化的,是公开的。所以为假单例),想要了解更多的,大家可以移步至: 🔜🔜🔜 javaEE Web(Tomcat深度理解 和 Servlet的本质_ChinaRainbowSea的博客-CSDN博客

现在我们可以比较清晰的回答 JSP是什么了 ?

    JSP是java程序。(JSP本质还是一个Servlet)
  • JSP是:JavaServer Pages的缩写。(基于Java语言实现的服务器端的页面。)
  • Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一。
  • JSP是一套规范。所有的web容器/web服务器都是遵循这套规范的,都是按照这套规范进行的“翻译”
  • 每一个web容器/web服务器都会内置一个JSP翻译引擎。这里所谓的翻译指的是: 将我们访问的 jsp 文件翻译成 对应的以.java 后缀结尾的Java文件,再将 该 java 文件编译生成 .class 文件。

问题:JSP第一次访问的时候是比较慢的,为什么?

同问: 为什么大部分的运维人员在给客户演示项目的时候,为什么提前先把所有的jsp文件先访问一遍。

    第一次比较麻烦的地方:
    • 要把jsp文件翻译生成java源文件
    • java源文件要编译生成class字节码文件
    • 然后通过class去创建servlet对象
    • 然后调用servlet对象的init方法
    • 最后调用servlet对象的service方法。
  • 第二次就比较快了,为什么?
      因为第二次直接调用单例servlet对象的service方法即可。

注意点:

开发JSP的最高境界: 眼前编写的是JSP代码,但是我们心里,脑袋里面当中呈现的是 java代码。

JSP既然本质上是一个Servlet,那么JSP和Servlet到底有什么区别呢?

    Servlet的职责是什么:收集数据。(Servlet的强项是后端的逻辑处理,业务处理,然后链接数据库,获取/收集数据。)
  • JSP的职责是什么:展示数据。(JSP的强项是做 前端数据的展示)

4. JSP 的基础语法

上面我们学习,了解到了 JSP 的本质原理,下面我们就来学习一下有关 JSP的基础语法吧

4.1 在 JSP 文件中直接编写文字

如下我们实验一下,我们在上一个 index.jsp 文件当中,直接编写一个 “Hello Wrold” 。当我们浏览器端访问该 index.jsp 文件时,浏览器端有是显示一个则样的效果,而其本质中 “Hello Wrold” 又是被翻译到了,成了什么,翻译到了哪里。

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 06:10:10 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;

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

  private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
          jakarta.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("jakarta.servlet";
    _jspx_imports_packages.add("jakarta.servlet.http";
    _jspx_imports_packages.add("jakarta.servlet.jsp";
    _jspx_imports_classes = null;
  }

  private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
      throws java.io.IOException, jakarta.servlet.ServletException {

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

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html";
      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("Hello World";
    } catch (java.lang.Throwable t {
      if (!(t instanceof jakarta.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;
    }
  }
}

servlet类的 service( 方法的 out.write("翻译到这里",直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。

补充一点: 解决浏览器端显示的乱码问题 ?

    通过page指令来设置响应的内容类型,在内容类型的最后面添加:charset=UTF-8
    • <%@page contentType="text/html;charset=UTF-8"%>,表示响应的内容类型是text/html,采用的字符集UTF-8
    • <%@page import="java.util.List,java.util.ArrayList"%> 表示到对应的 包信息

在JSP中编写的HTML CSS JS代码,这些代码对于JSP来说只是一个普通的字符串。但是JSP把这个普通的字符串一旦输出到浏览器,浏览器就会对HTML CSS JS进行解释执行。展现一个效果。举例如下:

<%@page contentType="text/html; chatSet=UTF-8" %>

<html>

<head>
    <title>my first JSP page</title>

    <script type="text/javascript">
        function sayHello( {
            window.alert("你好 JSP"
        }
    </script>
</head>
<bady>
    <h2>My first JSP page</h2>
    <input type="button" value="hello jsp" onclick="sayHello(" />

    fajsi ffasdfda
    fasd
    afjsfi9

    Sytstem.out.println("hello JSP";
</bady>
</html>

注意:都是被翻译到了 service(方法体当中去了

 public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
      throws java.io.IOException, jakarta.servlet.ServletException {

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

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.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("\r\n";
      out.write("\r\n";
      out.write("<html>\r\n";
      out.write("\r\n";
      out.write("<head>\r\n";
      out.write("    <title>my first JSP page</title>\r\n";
      out.write("\r\n";
      out.write("    <script type=\"text/javascript\">\r\n";
      out.write("        function sayHello( {\r\n";
      out.write("            window.alert(\"你好 JSP\"\r\n";
      out.write("        }\r\n";
      out.write("    </script>\r\n";
      out.write("</head>\r\n";
      out.write("<bady>\r\n";
      out.write("    <h2>My first JSP page</h2>\r\n";
      out.write("    <input type=\"button\" value=\"hello jsp\" onclick=\"sayHello(\" />\r\n";
      out.write("\r\n";
      out.write("    fajsi ffasdfda\r\n";
      out.write("    fasd\r\n";
      out.write("    afjsfi9\r\n";
      out.write("\r\n";
      out.write("    Sytstem.out.println(\"hello JSP\";\r\n";
      out.write("</bady>\r\n";
      out.write("</html>";

      
    } catch (java.lang.Throwable t {
      if (!(t instanceof jakarta.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;
    }
  }
}

4.2 在JSP中编写Java程序 <% %> 与 <%! %>

4.2.1 <% %>
<% 
 
// java语句;
// 在这里编写Java程序,编写 Java语句。
// 既然是编写 Java语句,自然要遵循Java的语法规范。
// 在这里编写的java语句,会被翻译到 service( 方法体内
%>

在这个<% java语句; %>符号当中编写的被视为java程序,被翻译到Servlet类的 service方法体内部。举例如下:


<%
    System.out.println("Hello World";
%>


<%
    System.out.println("Hello JSP";
%>

<%
    System.out.println("你好世界";
%>

访问 浏览器前端访问:demo01.jsp 效果

如下是 对应我们访问 demo01.jsp 文件翻译为 demo01_jsp.java 文件的源码信息如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 06:43:13 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;

public final class demo01_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
          jakarta.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("jakarta.servlet";
    _jspx_imports_packages.add("jakarta.servlet.http";
    _jspx_imports_packages.add("jakarta.servlet.jsp";
    _jspx_imports_classes = null;
  }

  private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
      throws java.io.IOException, jakarta.servlet.ServletException {

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

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.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(' ';
      out.write(' ';
      out.write(' ';
      out.write('\r';
      out.write('\n';

    System.out.println("Hello World";

      out.write("\r\n";
      out.write("\r\n";
      out.write("\r\n";

    System.out.println("Hello JSP";

      out.write("\r\n";
      out.write("\r\n";

    System.out.println("你好世界";

      out.write('\r';
      out.write('\n';
    } catch (java.lang.Throwable t {
      if (!(t instanceof jakarta.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代码的时候,你要时时刻刻的记住你正在“sevice( 方法体”当中写代码,方法体中可以写什么,不可以写什么,你心里是否明白呢?
    在service方法当中编写的代码是有顺序的,方法体当中的代码要遵循自上而下的顺序依次逐行执行。
  • service方法当中不能写静态代码块,不能写方法,不能定义成员变量。
  • 在同一个JSP当中 <% %> 这个符号可以出现多个。
4.2.2 <%! %>
<%!
	// 在这个符号当中编写的java程序会自动翻译到service方法之外。
    // service( 方法体之外是什么,自然就是在 Servlet 类里面了。
%>
    <%! %>在这个符号当中编写的java程序会自动翻译到service方法之外。简单的说就是: Servlet 类当中,service( 方法外,那不就是 成员变量,方法,代码块了。

举例如下:

启动部署,访问运行如下

    这个语法很少用,为什么?并且也不建议使用,因为在service方法外面写静态变量和实例变量,都会存在线程安全问题,因为JSP就是servlet,servlet是单例的,多线程并发的环境下的,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题。

4.3 通过JSP当中的 <%= %>向浏览器前端输入 Java变量

错误方式: 直接在 jsp 当中编写对应变量名,因为我们的在 JSP文件当中直接编写的文字的内容是被翻译到了 service( 方法体当中去了被 out.write("</html>" 包裹的。所以翻译的不是对应变量的值,而是对应变量的名(以字符串的形式翻译到了浏览器端了)。举例如下:

正确的方式一:不推荐

<% 
String name = “jack”; 
out.write("name = " + name; 
%>

注意: 以上代码中的 out是JSP的九大内置对象之一。可以直接拿来用。当然,必须只能在 service(方法内部使用(因为 JSP的内置对象是定义在 service( 方法体内的,所以其作用域也仅仅只是在 service( 方法体内部,才有效的)。

  • 如果输出的内容中含有“java代码当中的变量的值”,这个时候可以使用以下语法格式:

    <%=变量名%>  // 在=的后面编写要输出的内容。
    

    <%= %> 注意:在=的后面编写要输出的内容。

<%= %> 这个符号会被翻译到哪里?最终翻译成什么?

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 11:55:08 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 jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;

public final class demo02_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
          jakarta.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("jakarta.servlet";
    _jspx_imports_packages.add("jakarta.servlet.http";
    _jspx_imports_packages.add("jakarta.servlet.jsp";
    _jspx_imports_classes = null;
  }

  private volatile jakarta.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 jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response
      throws java.io.IOException, jakarta.servlet.ServletException {

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

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.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(' ';
      out.write("\r\n";
      out.write("\r\n";

    String name = "张三";

      out.write("\r\n";
      out.write("\r\n";
      out.print(name;
      out.write("\r\n";
      out.write("<br>\r\n";
      out.print( 1 + 2;
      out.write("\r\n";
      out.write("\r\n";
    } catch (java.lang.Throwable t {
      if (!(t instanceof jakarta.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;
    }
  }
}

翻译到service方法当中了。

什么时候使用<%=%> 输出呢?输出的内容中含有java的变量,输出的内容是一个动态的内容,不是一个死的字符串。如果输出的是一个固定的字符串,直接在JSP文件中编写即可。

4.4 在JSP 中的专业注释

    在JSP中如何编写JSP的专业注释
<%--JSP的专业注释,不会被翻译到java源代码当中。--%> 不会翻译对应的信息。该注解当中的内容会被忽略而不翻译的。

<!--这种注释属于HTML的注释,这个注释信息仍然会被翻译到java源代码当中,不建议。-->  翻译到HTML当中作为 html 的
注解存在。

4.5 JSP基础语法总结:

    JSP中直接编写普通字符串
翻译到service方法的out.write("这里"
    <%%>
翻译到service方法体内部,里面是一条一条的java语句。
    <%! %>
翻译到service方法之外。
    <%= %>
翻译到service方法体内部,翻译为:out.print(;
    <%@page contentType="text/html;charset=UTF-8"%>
page指令,通过contentType属性用来设置响应的内容类型以及字符集编码的设置。

JSP文件的扩展名必须是xxx.jsp吗 ?

    .jsp文件的扩展名是可以配置的。不是固定的。

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

xxx.jsp文件对于 Tomcat 小猫咪来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。

5. JSP的指令

指令的作用: 指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)

    include指令: 包含指令,在JSP中完成静态包含,很少用了。(这里不讲)
  • taglib指令: 引入标签库的指令。这个到JJSTL标签库的时候再学习。现在先不管。
  • page指令: 目前重点学习一个page指令。

指令的使用语法是什么?

<%@指令名  属性名=属性值  属性名=属性值  属性名=属性值....%>
// 这里我们只学习 page 指令所以是:
<%@page  属性名=属性值  属性名=属性值  属性名=属性值....%>

如下是:page指令当中一些 常用的属性

 <%@page session="true|false" %>
 true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。
 如果没有设置,默认值就是session="true"
 session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。
 session 表示的是会话机制。
 <%@page contentType="text/json" %>
 contentType属性用来设置响应的内容类型
 但同时也可以设置字符集。
 <%@page contentType="text/json;charset=UTF-8" %>
 <%-- 编码也可以单独分开设置如下--%>
 <%@page pageEncoding="UTF-8" %>
 pageEncoding="UTF-8" 表示设置响应时采用的字符集。
 <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %> 可以多个包一起导入
 <%@page import="java.util.*" %> 也可以单个导包
 import语句,导包。
 <%@page errorPage="/error.jsp" %>
 当前页面出现异常之后,跳转到error.jsp页面。
 errorPage属性用来指定出错之后的跳转位置。
 
  <%@page isErrorPage="true" %>
 表示启用JSP九大内置对象之一:exception
 默认值是false。 
 
 errorPage,和 isErrorPage 一般是连着一起使用的,因为 errorPage 表示错误跳转到指定的页面,但是不会有错误提示,而 isErrorPage 就是为了解决这个没有错误提示的问题。
 

举例:

注意:想要使用该 java.lang.Throwable exception 内置对象必须,配置相关的指令:

<%--
 在错误页面可以启用JSP九大内置对象,exception
 exeption内置对象就是刚刚发生的异常对象
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有两个参数一个是为:false,true :true 表示开启 isErrorPage,false 表示禁用,默认不设置也是禁用状态的--%>

error.jsp 的内容上的编写如下


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

<%--
 在错误页面可以启用JSP九大内置对象,exception
 exeption内置对象就是刚刚发生的异常对象
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有两个参数一个是为:false,true :true 表示开启 isErrorPage,false 表示禁用,默认不设置也是禁用状态的--%>

<html>
<head>
    <title>程序错误</title>
</head>
<body>
<h3>网络繁忙,稍后再试</h3>

<%
    // 配置了 isErrorPage 内置的异常对象则可以
    // 打印异常堆栈信息,输出到后台控制台上
    // exception 是JSP 九大内置对象之一。
    exception.printStackTrace(;
%>


</body>
</html>

6. JSP的九大内置对象

注意:所谓的JSP的九大内置对象指的是:在 service( 方法体当中的局部的对象变量,service( 方法体外是无法使用的。

  • jakarta.servlet.http.HttpServletRequest request 请求作用域

  • jakarta.servlet.ServletContext application 应用作用域

    • pageContext < request < session < application
    • 以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法。
    • 以上作用域的使用原则:尽可能使用小的域。
  • jakarta.servlet.ServletConfig config

  • jakarta.servlet.jsp.JspWriter out (负责输出)

思考一个问题:如果我只用JSP这一个技术,能不能开发web应用?

    当然可以使用JSP来完成所有的功能。因为JSP就是Servlet,在JSP的<%%>里面写的代码就是在service方法当中的,所以在<%%>当中完全可以编写JDBC代码,连接数据库,查询数据,也可以在这个方法当中编写业务逻辑代码,处理业务,都是可以的,所以使用单独的JSP开发web应用完全没问题。
  • 虽然JSP一个技术就可以完成web应用,但是不建议,还是建议采用servlet + jsp的方式进行开发。这样都能将各自的优点发挥出来。JSP就是做数据展示。Servlet就是做数据的收集。(JSP中编写的Java代码越少越好。)一定要职责分明。

7. 使用Servlet + JSP完成oa项目的改造

    使用Servlet处理业务,收集数据。 使用JSP展示数据。

  • 完成所有页面的正常流转。(页面仍然能够正常的跳转。修改超链接的请求路径。)

    • <%=request.getContextPath( %> 在JSP中动态的获取应用的根路径。
    • 遍历结果集的过程中,取出部门编号、部门名、位置等信息,封装成java对象。
  • 将java对象存放到List集合中。
  • 将List集合存储到request域当中。
  • 转发forward到jsp。
  • 在JSP中:

      从request域当中取出List集合。
  • 遍历List集合,取出每个部门对象。动态生成tr。
  • 模块对应的包目录如下:


    DeptServlet

    package com.RainbowSea.servlet;
    
    import com.RainbowSea.DBUtil.DBUtil;
    import com.RainbowSea.bean.Dept;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    // 可以使用模糊查询 @WebServlet("/dept/*"
    @WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"}
    public class DeptServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response throws ServletException,
                IOException {
    
            String servletPath = request.getServletPath(;  // 获取到浏览器当中的uri
    
            // 双重的判断,一个是 session 会话域要存在,其次是 会话域当中存储了名为 "username" 的信息
            if ("/dept/list".equals(servletPath {
                doList(request, response;
            } else if ("/dept/detail".equals(servletPath {
                doDetail(request, response;
            } else if ("/dept/delete".equals(servletPath {
                doElete(request, response;
            } else if ("/dept/save".equals(servletPath {
                doSave(request, response;
            } else if ("/dept/modify".equals(servletPath {
                doModify(request, response;
            }
    
    
        }
    
    
        /**
         * 修改部门信息
         *
         * @param request
         * @param response
         */
        private void doModify(HttpServletRequest request, HttpServletResponse response throws IOException {
            request.setCharacterEncoding("UTF-8";  // 设置获取的的信息的编码集
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            // 影响数据库的行数
            int count = 0;
    
    
            String deptno = request.getParameter("deptno";
            String dname = request.getParameter("dname";
            String loc = request.getParameter("loc";
    
    
            try {
                // 1. 注册驱动,连接数据库
                connection = DBUtil.getConnection(;
    
                // 2. 获取到操作数据库的对象,预编译sql语句,sql测试
                String sql = "update dept set dname = ?,loc = ? where depton = ?";
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 填充占位符,真正执行sql语句
                // 从下标 1开始
                preparedStatement.setString(1, dname;
                preparedStatement.setString(2, loc;
                preparedStatement.setString(3, deptno;
    
                count = preparedStatement.executeUpdate(;
            } catch (SQLException e {
                throw new RuntimeException(e;
            } finally {
                // 4. 释放资源,最后使用的优先被释放
                DBUtil.close(connection, preparedStatement, null;
            }
    
            if (count == 1 {
                // 更新成功
                // 跳转到部门列表页面(部门列表表面是通过java程序动态生成的,所以还需要再次执行另一个Servlet)
                // 转发是服务器内部的操作,“/” 不要加项目名
                // request.getRequestDispatcher("/dept/list/".forward(request,response;
    
                // 优化使用重定向,自发前端(需要指明项目名
                response.sendRedirect(request.getContextPath( + "/dept/list";
    
            }
        }
    
    
        /**
         * 保存部门信息
         *
         * @param request
         * @param response
         */
        private void doSave(HttpServletRequest request, HttpServletResponse response throws IOException {
    
            request.setCharacterEncoding("UTF-8";
    
            // 获取到前端的数据,建议 name 使用复制
            String deptno = request.getParameter("deptno";
            String dname = request.getParameter("dname";
            String loc = request.getParameter("loc";
    
            // 连接数据库,添加数据
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            // 影响数据库的行数
            int count = 0;
    
            try {
                // 1. 注册驱动,连接数据库
                connection = DBUtil.getConnection(;
    
                // 2. 获取操作数据库对象,预编译sql语句,Sql测试
                String sql = "insert into dept(depton,dname,loc values(?,?,?";
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 填充占位符, 真正执行sql语句,
                // 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
                preparedStatement.setString(1, deptno;
                preparedStatement.setString(2, dname;
                preparedStatement.setString(3, loc;
    
                // 返回影响数据库的行数
                count = preparedStatement.executeUpdate(;
    
                // 5.释放资源
            } catch (SQLException e {
                throw new RuntimeException(e;
            } finally {
                DBUtil.close(connection, preparedStatement, null;
            }
    
            // 保存成功,返回部门列表页面
            if (count == 1 {
                // 这里应该使用,重定向
                // 这里用的转发,是服务器内部的,不要加项目名
                //request.getRequestDispatcher("/dept/list/".forward(request, response;
    
                // 重定向
                response.sendRedirect(request.getContextPath( + "/dept/list";
            }
        }
    
    
        /**
         * 通过部门删除部门
         *
         * @param request
         * @param response
         */
        private void doElete(HttpServletRequest request, HttpServletResponse response throws IOException {
    
            request.setCharacterEncoding("UTF-8";  // 设置获取的的信息的编码集
            // 获取到发送数据
            String deptno = request.getParameter("deptno";
    
             /*
            根据部门编号删除信息,
            删除成功,跳转回原来的部门列表页面
            删除失败,跳转删除失败的页面
             */
    
            Connection connection = null;
            PreparedStatement preparedStatement = null;
    
            // 记录删除数据库的行数
            int count = 0;
    
    
            // 连接数据库进行删除操作
    
            try {
                // 1.注册驱动,连接数据库
                connection = DBUtil.getConnection(;
    
                // 开启事务(取消自动提交机制),实现可回滚
                connection.setAutoCommit(false;
    
                // 2. 预编译sql语句,sql测试
                String sql = "delete from dept where depton = ?"; // ? 占位符
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 填充占位符,真正的执行sql语句
                preparedStatement.setString(1, deptno;
                // 返回影响数据库的行数
                count = preparedStatement.executeUpdate(;
                connection.commit(;  // 手动提交数据
            } catch (SQLException e {
                // 遇到异常回滚
                if (connection != null {
                    try {
                        // 事务的回滚
                        connection.rollback(;
                    } catch (SQLException ex {
                        throw new RuntimeException(ex;
                    }
                }
                throw new RuntimeException(e;
            } finally {
                // 4. 释放资源
                // 因为这里是删除数据,没有查询操作,所以 没有 ResultSet 可以传null
                DBUtil.close(connection, preparedStatement, null;
            }
    
            if (count == 1 {
                // 删除成功
                // 仍然跳转到部门列表页面
                // 部门列表页面的显示需要执行另外一个Servlet,怎么办,可以使用跳转,不过这里最后是使用重定向
                // 注意:转发是在服务器间的,所以不要加“项目名” 而是 / + web.xml 映射的路径即可
                //request.getRequestDispatcher("/dept/list/".forward(request,response;
    
                // 优化:使用重定向机制 注意: 重定向是自发到前端的地址栏上的,前端所以需要指明项目名
                // 注意: request.getContextPath( 返回的根路径是,包含了 "/" 的
                response.sendRedirect(request.getContextPath( + "/dept/list";
            }
        }
    
    
        /**
         * 通过部门编号,查询部门的详情
         *
         * @param request
         * @param response
         */
        private void doDetail(HttpServletRequest request, HttpServletResponse response throws ServletException, IOException {
    
            request.setCharacterEncoding("UTF-8";  // 设置获取的的信息的编码集
    
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            // 获取到部门编号
            String dno = request.getParameter("dno";
            Dept dept = new Dept(;
    
            // 获取到部门编号,获取部门信息,将部门信息收集好,然后跳转到JSP做页面展示
    
    
            try {
                // 2. 连接数据库,根据部门编号查询数据库
                // 1.注册驱动,连接数据库
                connection = DBUtil.getConnection(;
    
                // 2. 预编译SQL语句,sql要测试
                String sql = "select dname,loc from dept where depton = ?";  // ? 占位符
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 填充占位符,真正执行sql语句
                preparedStatement.setString(1, dno;
                resultSet = preparedStatement.executeQuery(;
    
                // 4. 处理查询结果集
                while (resultSet.next( {
                    String dname = resultSet.getString("dname";
                    String loc = resultSet.getString("loc";
    
                    // 封装对象(建议使用咖啡豆,因为只有一个对象
                    dept.setDeptno(dno;
                    dept.setDname(dname;
                    dept.setLoc(loc;
                }
            } catch (SQLException e {
                throw new RuntimeException(e;
            } finally {
                // 5. 释放资源
                DBUtil.close(connection, preparedStatement, resultSet;
            }
    
            // 这个咖啡豆只有一个,所以不需要袋子,只需要将这个咖啡豆放到request请求域当中,
            // 用于对应的 jsp显示
            request.setAttribute("dept", dept;
            //String sign = request.getParameter("f";
    
            /*if("m".equals(sign {
    
                // 转发:多个请求为一个请求(地址栏不会发生改变
                // 注意: 该路径默认是从 web 开始找的 / 表示 web
                // 转发到修改页面
                request.getRequestDispatcher("/edit.jsp".forward(request,response;
            } else if("d".equals(sign {
                // 跳转到详情页面
                request.getRequestDispatcher("/detail.jsp".forward(request,response;
            }*/
    
            // 或者优化
            // 注意 无论是转发还是重定向都是从 “/” 开始的
            // request.getParameter(拿到的是 f=edit,还是f=detail 就是跳转到的哪个页面
            //<a href="<%=request.getContextPath(%>/dept/detail?f=edit&dno=<%=dept.getDeptno(%>">修改</a>
            //<a href="<%=request.getContextPath(%>/dept/detail?f=detail&dno=<%=dept.getDeptno(%>">详情</a>
            String forward = "/" + request.getParameter("f" + ".jsp";
            request.getRequestDispatcher(forward.forward(request, response;
        }
    
    
        /**
         * 连接数据库,查询所有的部门信息,将部门信息收集好,然后跳转到JSP页面展示
         *
         * @param request
         * @param response
         */
        private void doList(HttpServletRequest request, HttpServletResponse response throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8";  // 设置获取的的信息的编码集
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
    
            // 创建一个集合List 存储查询到的信息
            List<Dept> depts = new ArrayList<Dept>(;
    
    
            try {
                // 连接数据库,查询所有部门:
                // 1. 注册驱动,获取连接
                connection = DBUtil.getConnection(;
                // 2. 获取操作数据库对象,预编译sql语句
                String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 执行sql语句
                resultSet = preparedStatement.executeQuery(;
    
                // 4. 处理查询结果集
                while (resultSet.next( {
                    String det = resultSet.getString("det";  // 有别名要使用别名
                    String dname = resultSet.getString("dname";
                    String loc = resultSet.getString("loc";
    
                    Dept dept = new Dept(det, dname, loc;
    
                    // 将部门对象放到List集合当中
                    depts.add(dept;
    
                }
            } catch (SQLException e {
                throw new RuntimeException(e;
            } finally {
    
                // 5. 关闭资源
                DBUtil.close(connection, preparedStatement, resultSet;
            }
    
    
            // 查询到数据,将数据提交给 list.jsp 显示数据
            // 将集合存储的数据放到请求域当中,用于其他Servlet 使用 jsp 也是Servelt
            request.setAttribute("depList", depts;
    
            // 转发(注意不要重定向,重定向无法共用 request 请求域当中的数据
            // 转发路径,/ 默认是从 web 目录开始找的
            request.getRequestDispatcher("/list.jsp".forward(request, response;
        }
    }
    
    

    UserServlet

    package com.RainbowSea.servlet;
    
    import com.RainbowSea.DBUtil.DBUtil;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    
    @WebServlet({"/user/login", "/user/exit"}
    public class UserServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response throws ServletException,
                IOException {
    
            // 获取到浏览器地址栏上的URL路径
            String servletPath = request.getServletPath(;
    
            if ("/user/login".equals(servletPath {
                doLogin(request, response;
            } else if ("/user/exit".equals(servletPath {
                doExit(request, response;
            }
    
    
        }
    
        private void doExit(HttpServletRequest request, HttpServletResponse response throws IOException {
        }
    
        protected void doLogin(HttpServletRequest request, HttpServletResponse response throws ServletException,
                IOException {
    
            // 一个用户登录验证的方式:验证用户名和密码是否正确
            // 获取用户名和密码
            // 前端提交是数据是:username=111&password=fads
            // 注意:post 提交的数据是在请求体当中,而get提交的数据是在请求行当中
    
            boolean success = false;  // 标识登录成功
    
            String username = request.getParameter("username";
            String password = request.getParameter("password";
    
            String exempt = request.getParameter("exempt";
    
            // 连接数据库验证用户名和密码
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
    
            try {
                // 1. 获取连接,注册驱动
                connection = DBUtil.getConnection(;
    
                // 2. 获取操作数据对象,预编译sql语句, ? 占位符不要加,“”,'' 单双引号,成了字符串了,无法识别成占位符了。
                String sql = "select username,password from t_user where username = ? and password = ?";
                preparedStatement = connection.prepareStatement(sql;
    
                // 3. 填充占位符,真正执行sql语句
                preparedStatement.setString(1, username;
                preparedStatement.setString(2, password;
    
                resultSet = preparedStatement.executeQuery(;
    
                // 4. 处理查询结果集
                // 只有一条结果集
                if (resultSet.next( {
                    // 登录成功
                    success = true;
                }
            } catch (SQLException e {
                throw new RuntimeException(e;
            } finally {
                // 5. 关闭资源,最后使用的最先关闭,
                DBUtil.close(connection, preparedStatement, resultSet;
            }
    
            // 登录成功与否
            if (success {
                response.sendRedirect(request.getContextPath( + "/dept/list";
            } else {
                // 失败,跳转到失败页面
                response.sendRedirect(request.getContextPath( + "/error.jsp";
            }
    
    
        }
    }
    
    

    所以的 html 页面替换为了对应的 jsp 文件

    add.html --> add.jsp

    <%@page contentType="text/html; charset=utf-8" %>
    
    
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>新增部门</title>
    </head>
    
    <body>
    <h2>新增部门</h2>
    <form action="<%=request.getContextPath(%>/dept/save" method="post">
      部门编号: <input type="text" name="deptno" /><br>
      部门名称: <input type="text" name="dname" /><br>
      部门位置: <input type="text" name="loc" /><br>
      <input type="submit" value="保存" />
    </form>
    </body>
    
    </html>
    

    detail.html --> detail.jsp

    <%@ page import="com.RainbowSea.bean.Dept" %>
    <%@page contentType="text/html; charset=UTF-8" %>
    
    
    <%
      // 获取到请求域当中的数据,
      Dept dept = (Dept request.getAttribute("dept";
    %>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>部门详情</title>
    </head>
    <body>
    <h2>部门详情</h2>
    部门编号: <%=dept.getDeptno(%> <br>
    部门名称: <%=dept.getDname(%><br>
    部门位置: <%=dept.getLoc(%><br>
    
    <input type="button" value="后退" onclick="window.history.back("  />
    </body>
    </html>
    

    edit.html --> edit.jsp

    <%@ page import="com.RainbowSea.bean.Dept" %>
    <%@page contentType="text/html; charset=UTF-8" %>
    
    <!DOCTYPE html>
    <html lang="en">
    
    
    <%
      // 获取到存储到请求域当的 部门信息
      Dept dept = (Dept request.getAttribute("dept";
    %>
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>修改部门</title>
    </head>
    
    <body>
    <h2>修改部门</h2>
    <form action="<%=request.getContextPath(%>/dept/modify" method="post">
      <!-- readonly 表示只读,不可修改的作用 -->
      部门编号: <input type="text" name="deptno" value="<%=dept.getDeptno(%>" readonly /><br>
      部门名称: <input type="text" name="dname" value="<%=dept.getDname(%>" /><br>
      部门位置: <input type="text" name="loc" value="<%=dept.getLoc(%>" /><br>
      <input type="submit" value="修改" />
    </form>
    
    </body>
    
    </html>
    

    error.html --> error.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ page session="false" %>
    <html>
    <head>
        <title>登录失败</title>
    </head>
    <body>
    
    登录失败,密码或账号错误: <a href="<%=request.getContextPath(%>/index.jsp">请重新登录</a>
    
    </body>
    </html>
    
    

    index.html --> index.jsp

    <%@page contentType="text/html; charset=UTF-8" %>
    <%--表示访问 jsp 的时候不生成 session 对象--%>
    <%@page session="false" %>  <%--该指令不是警用jsp内置对象当中的 session--%>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>欢迎使用OA系统</title>
    </head>
    <body>
    <%--<!--注意:对应前端的资源获取基本上都是要加项目名的,并且要"/"开始-->--%>
    <%--这种方式不好,将 项目名写死了,--%>
    <%--<a href="/servlet09/dept/list/">查看部门列表</a>--%>
    <%--使用request.getContextPath( 动态获取项目名的根路径带 / 的,
    注意哪里可以加 空格,哪里不能加空格--%>
    <%--<a href="<%=request.getContextPath(          %>/dept/list">查看部门列表</a>--%>
    
    <%--<%=request.getContextPath(%> &lt;%&ndash;out.print(request.getContextPath( 获取到该项目的根路径带有/的&ndash;%&gt;--%>
    
    
        <h2>Login in</h2>
        <hr>
        <form action="<%=request.getContextPath(%>/user/login" method="post">
            username: <input type="text" name="username" /><br>
            password: <input type="password" name="password" /> <br>
            免十天登录 <input type="checkbox" name="exempt" value="true" /> <br>
            <input type="submit" value="login" />
        </form>
    </body>
    </html>
    

    list.html ---> list.jsp

    <%@ page import="com.RainbowSea.bean.Dept" %>
    <%@ page import="java.util.List" %>  <%--这是导包在jSP 当中,并翻译为import导入对于的报--%>
    <%@page contentType="text/html; charset=utf-8" %>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>部门列表页面</title>
    </head>
    <script type="text/javascript">
        function del(dno {
            // 弹出确认框,用户点击确定,返回true,点击取消返回false
            var ok = window.confirm("亲,删了不可恢复哦!";
            if (ok {
                // 发送请求进行删除数据的操作
                // 在js代码当中如何发送请求给服务器
                //
                // document.location.href="请求路径"
                // document.location = "请求路径"
                // window.location.href = "请求路径"
                // window.location = "请求路径"
    
                // 注意是根据所传的部门编号删除数据的
                document.location.href = "<%=request.getContextPath(%>/dept/delete?deptno=" + dno;
    
            }
        }
    </script>
    
    <body>
    <h2 align="center">部门列表</h2>
    <%--显示登录的用户名信息--%>
    <h4><%=session.getAttribute("username"%></h4>  <%--注意这里使用的是 jsp 内置的 session 对象所以不可以,不要把 session 禁用了。--%>
    
    <a href="<%=request.getContextPath(%>/user/exit">安全退出系统(手动清除 session 会话信息</a>
    
    <table border="1px" align="center" width="50%">
        <tr>
            <th>序号</th>
            <th>部门编号</th>
            <th>部门名称</th>
        </tr>
    
        <%
            // 获取到 request 域当中的数据,并取出来,我们知道是什么类型的数据,直接强制转换
            List<Dept> depList = (List<Dept> request.getAttribute("depList";
    
            int i = 0;
            // 循环遍历List,取出数据
            for (Dept dept : depList {
                // 在后台输出测试
                //System.out.println(dept.getDname(;
                // 在浏览器显示 测试
                //out.write(dept.getDname( + "<br>";
    
    
        %>
        <%--    浏览器显示测试--%>
        <%--    <%=dept.getDname(%>--%>
    
    
        <tr>
            <td><%=++i%>
            </td>
            <td><%=dept.getDeptno(%>
            </td>
            <td><%=dept.getLoc(%>
            </td>
            <td>
                <!-- javascript:void(0保持<a>超链接的格式,同时不会发生跳转
                    我只是希望用户点击该超链接的时候执行一段js代码,不进行页面的跳转 -->
                <a href="javascript:void(0" onclick="del(<%=dept.getDeptno(%>">删除</a>
    <%--            <a href="<%=request.getContextPath(%>/dept/detail?f=m&dno=<%=dept.getDeptno(%>">修改</a>--%>
    <%--            <a href="<%=request.getContextPath(%>/dept/detail?f=d&dno=<%=dept.getDeptno(%>">详情</a>--%>
                <%-- 这里的修改,和详情都是需要 部门编号,我们可以进行一个统一处理,通过所传的不同的 f= 值作为
     标记,用于服务器端的 Servlet 判断转发处理到的--%>
                <%--            或者是--%>
                <a href="<%=request.getContextPath(%>/dept/detail?f=edit&dno=<%=dept.getDeptno(%>">修改</a>
                <a href="<%=request.getContextPath(%>/dept/detail?f=detail&dno=<%=dept.getDeptno(%>">详情</a>
            </td>
        </tr>
    
        <%
            }
        %>
    </table>
    <hr>
    <a href="<%=request.getContextPath(%>/add.jsp">新增部门</a>
    </body>
    
    </html>
    

    当前的oa应用存在的问题:

      • 加一个登录功能。登录成功的可以访问该系统,登录失败不能访问。
    • 实现登录功能:

        步骤1:数据库当中添加一个用户表:t_user
        • t_user表当中存储的是用户的登录信息,最基本的也包括:登录的用户名和登录的密码。
        • 密码一般在数据库表当中存储的是密文。一般不以明文的形式存储。(这里先使用明文方式。)
        • 向t_user表中插入数据。
    • 步骤2:再实现一个登录页面。
        登录页面上应该有一个登录的表单。有用户名和密码输入的框。
    • 用户点击登录,提交表单,提交用户名和密码。form是post方式提交。
  • 步骤3:后台要有一个对应的Servlet来处理登录的请求。
      登录成功:跳转到部门列表页面。
  • 登录失败:跳转到失败的页面。
  • 步骤4:再提供一个登录失败的页面。
    • 这个登录功能目前只是一个摆设,没有任何作用。只要用户知道后端的请求路径,照样可以在不登录的情况下访问。
  • 这个登录没有真正起到拦截的作用。怎么解决?
  • 解决方式:通过 session 会话机制,判断是否为同一个用户,并且登录过。因为涉及到篇幅内容的过多,所以详细内容大家可以移步至:🔜🔜🔜 B/S结构系统的会话机制(session_ChinaRainbowSea的博客-CSDN博客

    8. 补充:

    bean 是什么意思?

      javabean(java的logo是一杯冒着热气的咖啡。javabean被翻译为:咖啡豆)
    • java是一杯咖啡,咖啡又是由一粒一粒的咖啡豆研磨而成。
    • 整个java程序中有很多bean的存在。由很多bean组成。
    • 什么是javabean?实际上javabean你可以理解为符合某种规范的java类,比如:
      • 有无参数构造方法
      • 属性私有化
      • 对外提供公开的set和get方法
      • 实现java.io.Serializable接口
      • 重写toString
      • 重写hashCode+equals
      • ....
    • javabean其实就是java中的实体类。负责数据的封装。
    • 由于javabean符合javabean规范,具有更强的通用性。
    package com.RainbowSea.bean;
    
    import java.io.Serializable;
    import java.util.Objects;
    
    public class Dept implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        private String deptno;
        private String dname;
        private String loc;
    
    
        public Dept( {
        }
    
        public Dept(String deptno, String dname, String loc {
            this.deptno = deptno;
            this.dname = dname;
            this.loc = loc;
        }
    
    
        public String getDeptno( {
            return deptno;
        }
    
        public void setDeptno(String deptno {
            this.deptno = deptno;
        }
    
        public String getDname( {
            return dname;
        }
    
        public void setDname(String dname {
            this.dname = dname;
        }
    
        public String getLoc( {
            return loc;
        }
    
        public void setLoc(String loc {
            this.loc = loc;
        }
    
        @Override
        public boolean equals(Object o {
            if (this == o return true;
            if (!(o instanceof Dept return false;
            Dept dept = (Dept o;
            return Objects.equals(getDeptno(, dept.getDeptno( && Objects.equals(getDname(, dept.getDname( && Objects.equals(getLoc(, dept.getLoc(;
        }
    
        @Override
        public int hashCode( {
            return Objects.hash(getDeptno(, getDname(, getLoc(;
        }
    
    
        @Override
        public String toString( {
            return "Dept{" +
                    "deptno='" + deptno + '\'' +
                    ", dname='" + dname + '\'' +
                    ", loc='" + loc + '\'' +
                    '}';
        }
    }
    
    
    

    9. 总结:

      JSP 的本质:一个index_jsp类就是一个Servlet类。总结就是:一个 index.jsp 文件会被翻译为一个 index_jsp.java 类,而 该 翻译的 index_jsp.java类是 继承 了 org.apache.jasper.runtime.HttpJspBase 类的,而 org.apache.jasper.runtime.HttpJspBase 类 是继承了 HttpServlet 类的。这下逻辑就清晰了,JSP 就是 Servlet

      1. 在JSP文件中直接编写文字,翻译的是在 service(方法体内的,翻译到 servlet类的 service(方法的 out.write("翻译到这里",直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。
      2. JSP的注释:
      <%--JSP的专业注释,不会被翻译到java源代码当中。--%> 不会翻译对应的信息。该注解当中的内容会被忽略而不翻译的。
      
      <!--这种注释属于HTML的注释,这个注释信息仍然会被翻译到java源代码当中,不建议。-->  翻译到HTML当中作为 html 的
      注解存在。
      
      1. <% %> 在这个<% java语句; %>符号当中编写的被视为java程序,被翻译到Servlet类的 service方法体内部。注意方法体当中不可以编写什么内容。同时因为编写的是 Java代码需要遵从 Java的语法。
      <% 
       
      // java语句;
      // 在这里编写Java程序,编写 Java语句。
      // 既然是编写 Java语句,自然要遵循Java的语法规范。
      // 在这里编写的java语句,会被翻译到 service( 方法体内
      %>
      
      1. <%! %>在这个符号当中编写的java程序会自动翻译到service方法之外。简单的说就是: Servlet 类当中,service( 方法外,那不就是 成员变量,方法,代码块了。
      <%!
      	// 在这个符号当中编写的java程序会自动翻译到service方法之外。
          // service( 方法体之外是什么,自然就是在 Servlet 类里面了。
      %>
      
      1. <%= %>向浏览器前端输入 Java变量值,而不是变量名。
      <% 
      String name = “jack”; 
      out.write("name = " + name; 
      %>
      
    1. JSP指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)主要说明的是 page 指令。指令的格式:

    <%@指令名  属性名=属性值  属性名=属性值  属性名=属性值....%>
    // 这里我们只学习 page 指令所以是:
    <%@page  属性名=属性值  属性名=属性值  属性名=属性值....%>
    
    1. JSP 的九大内置对象:所谓的JSP的九大内置对象指的是:在 service( 方法体当中的局部的对象变量,service( 方法体外是无法使用的。
    2. JSP 第一次访问速度慢的原因:翻译为 java,并编译java,生成 class 文件
    3. JSP文件的扩展名必须不是xxx.jsp后缀吗,不是的,.jsp文`件的扩展名是可以配置的。
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
    
    1. 开发JSP的最高境界: 眼前编写的是JSP代码,但是我们心里,脑袋里面当中呈现的是 java代码。

    10. 最后:

    限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后悔有期

    编程笔记 » JSP 的本质原理解析:"编写的时候是JSP,心里想解读的是 java 源码"

    赞同 (75) or 分享 (0)
    游客 发表我的评论   换个身份
    取消评论

    表情
    (0)个小伙伴在吐槽