• JavaWeb[总结]


    文章目录

    一、Tomcat

    1. BS 与 CS 开发介绍

    1.1 BS 开发

    1. B: browser(浏览器, 种类太多 ff, chrome, ie, edge,)
    2. S: Server(服务端, 考虑很多)
    3. 示意图

    image-20231026110211787

    对 BS 的解读

    (1) 兼容性 , 因为浏览器的种类很多,发现你写的程序,在某个浏览器会出现问题,其它浏览器正常
    (2) 安全性, 通常情况下,BS 安全性不如 CS 好控制
    (3) 易用性, BS 好于 CS, 浏览器电脑有
    (4) 扩展性, BS 相对统一,只需要写 Server

    image-20231026104313960

    1.2 CS 开发

    1. C: Client(客户端)

    2. S: Server(服务端)

    示意图

    image-20231026110416606

    2. 浏览器访问 web 服务过程详解(面试题)

    2.1 回到前面的 JavaWeb 开发技术栈图

    image-20231026142847303

    2.2 浏览器访问 web 服务器文件的 UML时序图(过程) !

    image-20231026143000521

    二、动态 WEB 开发核心-Servlet

    1. 为什么会出现 Servlet

    • 引入我们动态网页(能和用户交互)技术 --> Servlet
    • 对 JavaWeb 技术体系的流程图改造说明(细化).[整体的概念]

    image-20231026172700613

    2. 什么是 Servlet

    • 什么是 Servlet

    Servlet 在开发动态 WEB 工程中,得到广泛的应用,掌握好 Servlet 非常重要了, Servlet(基石)是 SpringMVC 的基础

    • Servlet(Java 服务端小程序),它的特点
    1. 他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)
    2. 他是用java语言编写的, 本质就是Java类
    3. 他是按照Servlet规范开发的(除了tomcat->Servlet weblogic->Servlet)
    4. 功能强大,可以完成几乎所有的网站功能(在以前,我们老程员,使用Servlet开发网站) 技术栈要求高

    3. Servlet 在 JavaWeb 项目位置

    image-20231026172745345

    4. Servlet 基本使用

    4.1 Servlet开发方式说明

    1. servlet3.0 前使用 web.xml , servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml配置
    2. 如何看 servlet 版本

    image-20231026180706901

    4.2浏览器调用 Servlet 流程分析[重点]

    image-20231026180822630

    4.3 Servlet 生命周期

    • 主要有三个方法
    1. init() 初始化阶段
    2. service() 处理浏览器请求阶段
    3. destroy() 终止阶段
    • 示意图(比较重要,而且形象)

    image-20231026184539751

    • 初始化阶段(init 方法)

    Servlet 容器(比如:Tomcat) 加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例并调用 init()方法,init()方法只调用一次。

    image-20231026185241248

    Servlet 容器如果遇到上面的情况会重新装载 Servlet:

    1. Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加 1 1 表示装载的顺序
    2. 在 Servlet 容器启动后,浏览器首次向 Servlet 发送请求(这个前面说过)
    3. Servlet 重新装载时(比如 tomcat 进行 redeploy【redeploy 会销毁所有的 Servlet 实例】),浏览器再向 Servlet 发送请求时,相当于第 1 次请求,先实例化Servlet --》 再调用 init(…) 方法 --》 调用service(…)方法
    • 处理浏览器请求阶段(service 方法)
    1. 每收到一个 http 请求,服务器就会产生一个新的线程取处理[线程]
    2. 创建一个用于封装 HTTP请求 消息的 ServletRequest 对象和一个代表 HTTP响应 消息的 ServletResponse 对象
    3. 然后调用 Servlet 的 service() 方法并将请求和响应对象作为参数传递进去
    • 终止阶段 destroy方法(体现 Servlet 完整的生命周期)

    当 web 应用被终止,或者 Servlet 容器终止运行,或者 Servlet 类重新装载时,会调用 destroy() 方法,比如重启 Tomcat、或者 redeploye web (重新发布)应用。

    • 上个图,切记一定要自己练习一遍

    工程路径:D:\IDEA_code\xjz_javaweb\servlet

    image-20231026190454648

    6. ServletConfig

    6.1 基本介绍

    1. ServletConfig 类是为 Servlet 程序的配置信息的类
    2. Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建
    3. Servlet 程序默认是第 1 次访问的时候创建,ServletConfig 在 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

    6.2 ServletConfig 能干什么

    1. 获取 Servlet 程序的 servlet-name 的值

    2. 获取初始化参数 init-param

    3. 获取 ServletContext 对象

    应用实例…

    示意图(思路分析)

    image-20231031161546465

    7. ServletContext

    7.1 为什么需要 ServletContext

    1. 先看一个需求: 如果我们希望统计某个 web 应用的所有 Servlet 被访问的次数,怎么办

    2. 方案 1-DB

    image-20231031171859412

    1. 方案 2-ServletContext

    image-20231031171922978

    由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过 ServletContext 对象来实现多个 Servlet 间通讯。ServletContext 对象通常也被称之为域对象。【示意图】

    image-20231031172032250

    详情看 韩顺平JavaWeb-笔记

    7.2 应用实例-简单的网站访问次数计数器

    1. 需求:完成一个简单的网站访问次数计数器

    image-20231031175539910

    1. 使用 Chrome 访问 payServlet, 每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示
    2. 使用火狐访问 OrderServlet,每访问一次,就增加 1 访问次数,在后台输出,并将结果返回给浏览器显示

    image-20231031175623372

    代码如下D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\servletcontext

    运行结果

    image-20231031181128163

    8. HttpServletRequest

    8.1 HttpServletRequest介绍

    1. HttpServletRequest 对象代表客户端的请求
    2. 当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象中
    3. 通过这个对象的方法,可以获得客户端这些信息。

    我们是OOP程序员,第一步,看它的类图(继承关系和方法)

    8.2 HttpServletRequest 常用方法

    1. getRequestURI() 获取请求的资源路径 http://localhost:8080**/servlet/loginServlet**

    2. getRequestURL() 获 取 请 求 的 统 一 资 源 定 位 符 ( 绝 对 路 径 )
      http://localhost:8080/servlet/loginServlet

    3. getRemoteHost() 获取客户端的 主机, getRemoteAddr()

    4. getHeader() 获取请求头

    5. getParameter() 获取请求的参数

    6. getParameterValues() 获取请求的参数(多个值的时候使用) , 比如 checkbox, 返回的数组

    7. getMethod() 获取请求的方式 GET 或 POST

    8. setAttribute(key, value); 设置域数据

    9. getAttribute(key); 获取域数据

    10. getRequestDispatcher() 获取请求转发对象, 请求转发的核心对象

    8.3 应用实例

    package com.xjz.servlet.request;
    
    import com.sun.org.apache.regexp.internal.RE;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class HttpServletRequestMethods extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //这里我们使用 request 对象,获取表单提交的各种数据
            System.out.println("HttpServletRequestMethods doPost() 被调用...");
    
            /***********************************
             * 获取和 http 请求头相关信息
             ***********************************/
            System.out.println("请求的资源路径 URI= " + request.getRequestURI()); ///servlet/requestMethods
            //http://主机/uri
            System.out.println("请求的统一资源定位符(决定路径) URL= " + request.getRequestURL());// http://localhost:8080/servlet/requestMethods
            System.out.println("请求的客户端 ip地址=" + request.getRemoteAddr());//本地就是 127.0.0.1
            //思考题:如发现某个 ip 在 10s 中,访问的次数超过 100 次,就封 ip
            //实现思路:1 用一个集合 concurrentHashmap[ip:访问次数] 2[线程/定时扫描]3 做成处理
            System.out.println("http 请求头 HOST=" + request.getHeader("Host")); //HOST=localhost:8080
            //说明:如果我们希望得到请求头的相关信息,可以使用request.getHeader(""请求头字段)
            System.out.println("该请求的发起地址是=" + request.getHeader("Referer"));//
            //请获取访问网站的浏览器是什么?
            String userAgent = request.getHeader("User-agent");
            System.out.println("userAgent=" + userAgent);
            String[] s = userAgent.split(" ");
            System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
            // 主要是 Get / Post
            System.out.println("http 请求方式= " + request.getMethod());
    
            /***********************************
             * 获取和请求参数相关信息, 注意要求在返回数据前,获取参数
             ***********************************/
            //1. 获取表单的数据[单个数据]
            //username=tom&pwd=123&hobby=lyl&hobby=lzq
            String username = request.getParameter("username");
            String pwd = request.getParameter("pwd");
    
            //2. 获取表单的一组数据
            String[] hobbies = request.getParameterValues("hobby");
            System.out.println("username= " + username);
            System.out.println("pwd= " + pwd);
            //增强 for 循环的快捷键 iter->回车即可 , 能使用快捷键,就使用快捷键
            for (String hobby : hobbies) {
                System.out.println("hobby= " + hobby);
            }
    
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //合并
            doPost(request, response);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    8.4 注意事项和细节

    1. 获 取 doPost 参 数 中 文 乱 码 解 决 方 案 , 注 意 setCharacterEncoding(“utf-8”) 要 写 在request.getParameter()前

    2. 注意:如果通过 PrintWriter writer, 有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)

    3. 处理 http 响应数据中文乱码问题

    4. 再次理解 Http 协议响应 Content-Type 的含义, 比如 text/plain application/x-tar**

    image-20231031202754895

    8.5 请求转发[重要]

    8.5.1 为什么需要请求转发
    1. 目前我们学习的都是一次请求,对应一个 Servlet,如图

    image-20231101091756835

    1. 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成一个任务(Servlet 链, 流水作业) 如图:

    image-20231101091823166

    8.5.2 请求转发声明
    1. 实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理
    2. HttpServletRequest 对象(也叫 Request 对象)提供了一个 getRequestDispatcher 方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的 forward 方法可以实现请求转发
    3. request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据通过 request 对象带给其它 web 资源处理
    • setAttribute方法
    • getAttribute方法
    • removeAttribute方法
    • getAttributeNames方法
    8.5.3 实现请求转发原理图

    请求转发原理示意图

    image-20231101092006717

    8.5.4 请求转发注意事项和细节
    1. 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
    2. 同一次 HTTP 请求中,进行多次转发仍然是一次 HTTP 请求
    3. 同一次 HTTP 请求中,进行多次转发多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
    4. 可以转发到 WEB-INF 目录下(后面做项目使用)
    5. 不能访问当前 WEB 工程外的资源
    6. 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付[演示]

    9. HttpServletResponse

    9.1 HttpServletResponse介绍

    1. 每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用。

    2. HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse 对象来进行设置即可

    image-20231101112023126

    9.2 HttpServletResponse 类图

    image-20231101112039703

    9.3 向客户端返回数据方法

    image-20231101112113532

    1. 字节流 getOutputStream(); 常用于下载(处理二进制数据)
    2. 字符流 getWriter(); 常用于回传字符串
    3. (细节:)两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错

    9.4 请求重定向[重要]

    9.4.1 请求重定向原理示意图
    1. 请求重定向指:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web资源,这称之为请求重定向

    2. 请求重定向原理示意图

    image-20231101112318229

    相关代码路径:D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\response\DownServlet.java

    D:\IDEA_code\xjz_javaweb\servlet\src\com\xjz\servlet\response\DownServletNew.java

    9.4.2 请求重定向注意事项和细节
    1. 最佳应用场景:网站迁移,比如原域名是 www.baidu.com 迁移到 www.baidu.cn ,但是百度抓取的还是原来网址.
    2. 浏览器地址会发生变化,本质是两次 http 请求.
    3. 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
    4. 不能重定向到 /WEB-INF 下的资源
    5. 可以重定向到 Web 工程以外的资源, 比如 到 www.baidu.com 【在前面的案例演示】
    6. 重定向有两种方式, 推荐使用第 1 种.
    response.sendRedirect("/servlet/downServletNew");
    
    //第二种重定向的写法
    response.setStatus(302);//设置 http响应的状态码
    //设置http响应的 Location: /servlet/downServletNew
    response.setHeader("Location","/servlet/downServletNew");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 动态获取到 application context
    //5. 动态获取到 application context
    String contextPath = getServletContext().getContextPath();
    System.out.println("contextPath=" + contextPath);//servlet
    //response.sendRedirect("/servlet/downServletNew");
    response.sendRedirect(contextPath + "/downServletNew");
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、WEB开发通信协议-HTTP 协议

    1. HTTP请求包分析(GET)

    image-20231031100515211

    2. HTTP请求包分析(POST)

    image-20231031101729411

    3. GET 请求和 POST请求分别有哪些?

    image-20231031102502107

    4. HTTP 请求中怎么选择 GET 和 POST 方式

    image-20231031103432547

    image-20231031103552321

    image-20231031103705581

    image-20231031104118237

    5. HTTP响应包分析

    • HTTP响应包括3个部分

      1. 响应行
      2. 响应头
      3. 响应体
    • HTTP响应包分析图

    image-20231031105231248

    6. 状态码

    6.1 状态码 301

    image-20231031111204817

    image-20231031111147111

    6.2 状态码 304

    image-20231031111046057

    7. MIME 类型

    7.1 MIME 介绍

    image-20231031111402343

    7.2 常见的 MIME 类型

    image-20231031111604737

    8. HTTP 作业

    image-20231031160144923

    四、WEB 工程路径专题

    • 先看一个问题 => 怎么解决访问资源的问题

    image-20231103103736746

    • 工程路径解决方案
    1. 说明: 使用相对路径来解决, 一个非常重要的规则:页面所有的相对路径,在默认情况下,都会参考当前浏览器地址栏的路径 http://ip:port/工程名/ + 资源来进行跳转。所以我们可以直接这样写

    image-20231103103825131

    1. 相对路径带来的问题举例 => 示意图

    image-20231103103911373

    1. 如果需要指定页面相对路径参考的的路径,可以使用 base 标签来指定

    1. 解决方案: base 标签

    1.1 base 基本介绍

    1. base 标签是 HTML 语言中的基准网址标记,它是一个单标签,位于网页头部文件的 head标签内
    2. 一个页面最多只能使用一个 base 元素,用来提供一个指定的默认目标,是一种表达路径和连接网址的标记。
    3. 常见的 url 路径形式分别有相对路径与绝对路径,如果 base 标签指定了目标,浏览器将通过这个目标来解析当前文档中的所有相对路径,包括的标签有(a、img、link、form)
    4. 也就是说,浏览器解析时会在路径前加上 base 给的目标,而页面中的相对路径也都转换成了绝对路径。使用了 base 标签就应带上 href 属性和 target 属性

    1.2 应用实例

    D:\IDEA_code\xjz_javaweb\webpath\src\com\xjz\servlet

    1.3 WEB工程路径注意事项和细节

    1. Web 工程的相对路径和绝对路径

    image-20231103142227056

    1. 在实际开发中,路径都使用绝对路径,而不是相对路径
    2. 在 web 中 / 斜杠 如果被浏览器解析,得到的地址是:http://ip[域名]:port/ 比如: 斜杠
    3. 在 web 中 / 斜杠 如果被服务器解析,得到的地址是:http://ip[域名]:port/工程路径/,你也可以理解成 /工程路径/ 下面的几种情况就是如此:

    image-20231103142334641

    1. 在 javaWeb 中 路径最后带 / 和 不带 / 含义不同, 一定要小心,
      比如 网址 : servlet03 表示资源

    网址 : servlet03 表示路径

    1. 特别说明:重定向 response.sendRediect(“/”); 这条语句虽然是在服务器执行的,但是,服务器是把斜杠 / 发送给浏览器解析。因此得到地址 http://ip[域名]:port/

    小结: 在编写资源路径时: , 考虑这么几点
    (1) 这个路径 前面有没有 /
    (2) 这个路径 在哪里被解析 [服务器还是浏览器] , 如果前面有 / , 并且是在 浏览器被解析的 被解析成 http://ip:port/ , 如果在服务器端被解析 , 被解析成 /工程路径/
    (3) 如果这个路径,前面没有 / , 并且在浏览器被解析,则以浏览器当前的地址栏 去掉资源部分,作为一个相对路径.

    (4) 这个路径,最后有没有 / , 如果最后有/ 表示路径, 如果没有 / 表示资源

    五、Web 开发会话技术 -Cookie&Session

    1. 会话

    1.1 基本介绍

    1. 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个 web 资源,然后关闭浏览器,整个过程称之为一个会话。

    2. 会话过程中要解决的一些问题?

    1. 每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,服务器要想办法为每个用户保存这些数据
    2. 例如:多个用户点击超链接通过一个 servlet 各自购买了一个商品,服务器应该想办法把每一个用户购买的商品保存在各自的地方,以便于这些用户点结帐 servlet 时,结帐servlet 可以得到用户各自购买的商品为用户结帐。

    1.2 会话的两种技术

    Cookie(小甜饼)是客户端技术,服务器把每个用户的数据以 cookie 的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的 web 资源时,就会带着各自的数据去。这样,web 资源处理的就是用户各自的数据了。【简单示意图】

    image-20231103152046152

    2. cookie 介绍

    1. Cookie 是服务器在客户端保存用户的信息,比如登录名,浏览历史等, 就可以以 cookie方式保存.
    2. Cookie 信息就像是小甜饼(cookie 中文)一样,数据量并不大,服务器端在需要的时候可以从客户端/浏览器读取(http 协议),可以通过图来理解

    image-20231103152300148

    image-20231103152325913

    演示 Cookie 底层实现机制**,** 创建和读取 Cookie

    D:\IDEA_code\xjz_javaweb\cookie-session\src\com\xjz\cookie\CreateCookie.java

    image-20231103155504035

    3. Cookie 生命周期

    1. Cookie 的生命周期指的是如何管理 Cookie 什么时候被销毁(删除)

    2. setMaxAge()

    ● 正数,表示在指定的秒数后过期
    ● 负数,表示浏览器关闭,Cookie 就会被删除(默认值是-1)
    ● 0,表示马上删除 Cookie

    4 . Cookie 有效路径

    1. Cookie 有效路径 Path 的设置

    2. Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。 path属性是通过请求的地址来进行有效的过滤

    3. 规则如下

    5. Cookie注意事项和细节

    1. 一个 Cookie 只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
    2. 一个 WEB 站点可以给一个浏览器发送多个 Cookie,一个浏览器也可以存储多个 WEB 站点提供的 Cookie。
    3. cookie 的总数量没有限制,但是每个域名的 COOKIE 数量和每个 COOKIE 的大小是有限制的 (不同的浏览器限制不同, 知道即可) , Cookie 不适合存放数据量大的信息。
    4. 注意,删除 cookie 时,path 必须一致,否则不会删除
    5. Java servlet 中 cookie 中文乱码解决 [代码演示 EncoderCookie.java ReadCookie2.java]

    说明:如果存放中文的 cookie, 默认报错, 可以通过 URL 编码和解码来解决, 不建议存放中文的 cookie 信息

    package com.xjz.cookie;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.net.URLEncoder;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class EncoderCookie extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("EncoderCookie 被调用...");
            //1. 创建 Cookie,有中文
            //代码解读
            //1) 如果直接存放中文的 cookie,报错 Control character in cookie value or attribute.
            //2)  解决办法,就是将中文 编码成 URL编码  英文: Encode=编码
            //3) 编码后,再保存即可
            String name = URLEncoder.encode("程序员老徐", "utf-8");
    
            Cookie cookie = new Cookie("name", name);
    
            //2. 保存到浏览器
            response.addCookie(cookie);
    
            //3. 给浏览器返回信息
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.print("

    设置中文cookie成功~

    "
    ); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    解码

    package com.xjz.cookie;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.net.URLDecoder;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class ServletReadCookie2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            Cookie[] cookies = request.getCookies();
            //找到带有中文的 cookie
            Cookie name = CookieUtils.readCookieByName("name", cookies);
            //处理的中文乱码问题
            writer.println("Cookie[" + name.getName()
                    + "=" +
                    URLDecoder.decode(name.getValue(), "utf-8") + "] 
    "
    ); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    6. Session 有什么用

    1. 不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字,还可以随时去查看自己的购物车中的商品, 是如何实现的?
    2. 也就是说,一个用户在浏览网站不同页面时,服务器是如何知道是张三在浏览这个页面,还是李四在浏览这个页面?

    image-20231104122907291

    ● 解决之道—session 技术, 简单说

    1. Session 是服务器端技术,服务器在运行时为每一个用户的浏览器创建一个其独享的session 对象/集合
    2. 由于 session 为各个用户浏览器独享,所以用户在访问服务器的不同页面时,可以从各自的 session 中读取/添加数据, 从而完成相应任务

    7. Session 基本原理

    7.1 Session 原理示意图

    image-20231104123005077

    image-20231104123010846

    1. 当用户打开浏览器,访问某个网站, 操作 session 时,服务器就会在内存(在服务端)为该浏览器分配一个 session 对象,该 session 对象被这个浏览器独占, 如图
    2. 这个 session 对象也可看做是一个容器/集合,session 对象默认存在时间为 30min(这是在tomcat/conf/web.xml),也可修改

    image-20231104123115565

    7.2 Session 可以做什么

    1. 网上商城中的购物车
    2. 保存登录用户的信息
    3. 将数据放入到 Session 中,供用户在访问不同页面时,实现跨页面访问数据
    4. 防止用户非法登录到某个页面

    7.3 如何理解 Session

    1. session 存储结构示意图

    image-20231104131135843

    1. 你可以把 session 看作是一容器类似 HashMap,有两列(K-V),每一行就是 session 的一个属性。
    2. 每个属性包含有两个部分,一个是该属性的名字(String),另外一个是它的值(Object)

    8. session 常用方法

    1. 创建和获取 Session,API 一样HttpSession hs=request.getSession();第 1 次调用是创建 Session 会话, 之后调用是获取创建好的 Session 对象

    2. 向 session 添加属性
      hs.setAttribute(String name,Object val);

    3. 从 session 得到某个属性
      Object obj=hs.getAttribute(String name);

    4. 从 session 删除调某个属性:

    hs.removeAttribute(String name);

    1. isNew(); 判断是不是刚创建出来的 Session

    2. 每个 Session 都有 1 个唯一标识 Id 值。通过 getId() 得到 Session 的会话 id 值

    9. session 底层实现机制 *

    9.1 原理分析图

    • session 底层实现机制图解(重要)

    image-20231104140912216

    image-20231104140933012

    9.2 代码演示

    创建Session

    package com.xjz.session;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class CreateSession extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //        System.out.println("CreateSession 被调用..");
    
            //1. 获取session,同时也可以创建session
            HttpSession session = request.getSession();
            //2. 给session 获取id
            System.out.println("当前sessionId=" + session.getId());
            //3. 给session 存放数据
            session.setAttribute("key1", "value1");
    
            //4. 给浏览器发送一个回复
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println("

    创建/操作 session成功~"); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    • 运行结果

    image-20231104143215598

    image-20231104143301188

    第二次刷新后

    image-20231104143440409

    • 读session
    package com.xjz.session;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class ReadSession extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //        System.out.println("ReadSession 被调用..");
    
            //演示读取 session
            //1. 获取session,如果没有session,也会创建
            HttpSession session = request.getSession();
            //2. 读取属性
            Object key1 = session.getAttribute("key1");
            if (key1 != null){
                System.out.println("session属性 key1=" + (String) key1);
            } else {
                System.out.println("session中没有 key1属性 ");
            }
            //给浏览器回复一下
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println("

    读取session成功~

    "
    ); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    我们可以看出,两个servlet的JSEESIONID是同一JSESSIONID

    image-20231104144308695

    image-20231104144340653

    有了代码支撑,我们在回头看 Session 的原理图,就有更深刻的理解

    1.7.3 Session 实现原理动画

    • 服务器是如何实现一个 session 为一个用户浏览器服务的

    image-20231104144544113

    10. Session 生命周期

    10.1 session 生命周期-说明

    1. public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session 就会被销毁。
    2. 值为正数的时候,设定 Session 的超时时长。
    3. 负数表示永不超时
    4. public int getMaxInactiveInterval()获取 Session 的超时时间
    5. public void invalidate() 让当前 Session 会话立即无效
    6. 如果没有调用 setMaxInactiveInterval() 来指定 Session 的生命时长,Tomcat 会以 Session默认时长为准,Session 默认的超时为 30 分钟, 可以在 tomcat 的 web.xml 设置

    image-20231104160447765

    1. Session 的生命周期指的是 :客户端/浏览器两次请求最大间隔时长,而不是累积时长。即当客户端访问了自己的 session,session 的生命周期将从 0 开始重新计算。(代码解读: 指的是同一个会话两次请求之间的间隔时间)
    2. 底层: Tomcat 用一个线程来轮询会话状态,如果某个会话的空闲时间超过设定的最大值,则将该会话销毁

    10.2 Session 生命周期-应用实例

    • 需求:代码演示说明 Session 的生命周期
    1. 创建
    package com.xjz.session;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class CreateSession2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("CreateSession2 被调用..");
    
            //1. 创建 session
            HttpSession session = request.getSession();
            System.out.println("CreateSession2 sid= " + session.getId());
    
            //2. 设置生命周期为 60s
            session.setMaxInactiveInterval(60);
            session.setAttribute("u","jack");
    
            //3. 回复一下浏览器
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println("

    创建session成功,设置生命周期 60s

    "
    ); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    1. 读 Session
    package com.xjz.session;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class ReadSession2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("ReadSession2 被调用..");
    
            //1.获取到session
            HttpSession session = request.getSession();
            System.out.println("ReadSession2 sid= " + session.getId());
    
            //2. 读取session的属性
            Object u = session.getAttribute("u");
            if (u != null){
                System.out.println("获取到session属性 u=" + (String) u);
            } else {
                System.out.println("读取不到session属性 u 说明原来的session被销毁");
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    image-20231104161044125

    • 删除session 使用 invalidate() 方法
    package com.xjz.session;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class DeleteSession extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("DeleteSession 被调用..");
    
            //1. 演示如何删除session
            HttpSession session = request.getSession();
            session.invalidate();
    
            //2. 如果我们要删除的是 session的某个属性
            //session.removeAttribute("xxx");
    
            //3. 回复一下浏览器
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println("

    删除session成功

    "
    ); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    image-20231104161938398

    总结:Session 的生命周期

    1. 指的是两次访问 session 的最大间隔时间
    2. 如果你在 session 没有过期的情况下(默认30分钟),操作 session, 则会重新开始计算生命周期
    3. session 是否过期,是由服务器来维护和管理
    4. 如我们调用了 invaliate() 会直接将该 session 删除/销毁
    5. 如果希望删除 session 对象的某个属性, 使用 removeAttribute(“xx”)

    11. Session 经典案例-防止非法进入管理页面

    11.1 作业布置

    1. 需求说明:完成防止用户登录管理页面应用案例(如图)

    image-20231104175541725

    说明:

    1. 只要密码为 666666, 我们认为就是登录成功
    2. 用户名不限制
    3. 如果验证成功,则进入管理页面 ManageServelt.java ,否则进入 error.html
    4. 如果用户直接访问 ManageServet.java , 重定向到到 login.html

    11.2 作业代码

    D:\IDEA_code\xjz_javaweb\cookie-session\src\com\xjz\session\homework

    login.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面title>
    head>
    <body>
    <h1>用户登录h1>
    <form action="/cs/loginCheckServlet" method="post">
        用户名:<input type="text" name="username"><br/>
        密 码:<input type="password" name="pwd"><br/>
        <input type="submit" value="登录"/>
    form>
    body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    error.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录失败title>
    
        <base href="/cs/">
    head>
    <body>
    <h1>登录失败h1>
    <a href="login.html">点击返回重新登录a>
    
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    LoginCheckServlet.java

    package com.xjz.session.homework;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.*;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class LoginCheckServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //System.out.println("LoginCheckServlet 被调用..");
    
            //1. 获取提交表单的用户名和密码
            String username = request.getParameter("username");
            String pwd = request.getParameter("pwd");
    
    //        //2. 将用户名 存放到 cookie中,并响应给浏览器   ==> Cookie方法
    //        Cookie cookie = new Cookie("username",username);
    //        response.addCookie(cookie);
    
            //3. 只要密码为 66666,我们认为就是登录成功,用户名不限制
            if (pwd != null && pwd.length() != 0) {
                if ("666666".equals(pwd)) {
                    //2. 将用户名 存放到 session 中,并响应给浏览器   ==> Session方法 (生命周期默认30min)
                    //把用户名保存到 session
                    HttpSession session = request.getSession();
                    session.setAttribute("loginuser",username);
                    //超时1s 即销毁,否则在超时时间内,直接访问manageServlet2 可直接查看 管理员
                    session.setMaxInactiveInterval(1);
    
                    System.out.println("验证成功~");
                    //请求转发
                    //注意:请求转发 是在服务端请求的,默认路径是 http://localhost:8080/工程路径
                    //     所以不需要带 /工程路径, 如果带了,访问地址为http://localhost:8080/cs/cs/login.html
                    //     请求转发 不能访问 web以外的资源,如果是访问 servlet,则需要使用重定向!!
    //                request.getRequestDispatcher("/manageServlet.html").forward(request, response);
                    // session 方法
                    request.getRequestDispatcher("/manageServlet2").forward(request, response);
                } else {
                    //验证失败,密码不是666666,请求转发到 error.html
                    System.out.println("验证失败");
                    request.getRequestDispatcher("/error.html").forward(request, response);
                }
            } else {
                // 回复给浏览器
                response.setContentType("text/html;charset=utf-8");
                PrintWriter writer = response.getWriter();
                writer.println("

    密码不能为 null,请重新输入

    "
    ); writer.flush(); writer.close(); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    **ManageServlet.java **

    使用 Cookie方法,用户直接访问 ManageServlet.java , 重定向到到 login.html

    package com.xjz.session.homework;
    
    import com.xjz.cookie.CookieUtils;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.*;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     * 使用 Cookie方法,用户直接访问 ManageServlet.java , 重定向到到 login.html
     */
    public class ManageServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //        System.out.println("ManageServlet 被调用..");
    
            //获取referer,即从哪个网址请求过来的 url
            String referer = request.getHeader("Referer");
            System.out.println("referer=" + referer);
            //判断打过来的请求 url是否是 login.html,如果不是,重定向到login
            if ("http://localhost:8080/cs/login.html".equals(referer)) {
                //获取提交表单的 username
                Cookie[] cookies = request.getCookies();
                //读取key为 username 的 cookie
                Cookie usernameCookie = CookieUtils.readCookieByName("username", cookies);
                //获取该 cookie 的 value值
                String usernameVal = usernameCookie.getValue();
                System.out.println("usernameCookie= " + usernameVal);//xjz_2002
                // 给浏览器返回消息
                response.setContentType("text/html;charset=utf-8");
                PrintWriter writer = response.getWriter();
                writer.println("

    恭喜你,管理员:" + usernameVal + "

    "
    ); writer.flush(); writer.close(); } else { //如果用户直接访问 /manageServlet,重定向到 login.html //注意:请求重定向是 response响应到服务器的, 所以路径前必须加 /工程路径 //String contextPath = request.getContextPath();// 工程路径 => /cs response.sendRedirect(request.getContextPath() + "/login.html"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    ManageServlet2.java

    将用户名 存放到 cookie中,并响应给浏览器 ==> Session方法 (生命周期默认30min)

    package com.xjz.session.homework;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author xjz_2002
     * @version 1.0
     * 将用户名 存放到 session中,并响应给浏览器   ==> Session方法 (生命周期默认30min)
     */
    public class ManageServlet2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //判断该用户是否登录过
            HttpSession session = request.getSession();
            Object userlogin = session.getAttribute("loginuser");
            System.out.println("userlogin= " + userlogin);
            if (userlogin == null) { //说明该用户没有登录
                //重新登录 -> 请求重定向
    //            response.sendRedirect("/cs/login.html");
                response.sendRedirect(request.getContextPath() + "/login.html");
                return;
            }
    
            response.setContentType("text/html;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.println("

    恭喜你,管理员:" + userlogin.toString() + "

    "
    ); writer.flush(); writer.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    六、JavaWeb三大组件之监听器 Listener

    1. Listener 监听器介绍

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

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

    3. 监听器的作用是,监听某种变化(一般就是对象创建/销毁, 属性变化), 触发对应方法完成相应的任务

    4. JavaWeb 中的监听器(共八个), 目前最常用的是ServletContextListener

    2. JavaWeb 的监听器

    2.1 ServletContextListener 监听器

    1. 作用:监听 ServletContext 创建或销毁(当我们 Web 应用启动时,就会创建 ServletContext),即生命周期监听,应用场景(1)加载初始化的配置文件;比如 spring 的配置文件 (2)任务调度(配合定时器 Timer/TimerTask)

    2. 相关方法

    image-20231107111416139

    1. 配置 web.xml

    image-20231107111511843

    2.2 ServletContextAttributeListener 监听器

    1. 作用:监听 ServletContext 属性变化

    2. 相关方法

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    2.3 其他监听器-使用较少

    2.3.1 HttpSessionListener 监听器

    1. 作用:监听 Session 创建或销毁,即生命周期监听

    2. 相关方法

    image-20231107155919741

    1. 使用方法和前面一样, 可以用于监控用户上线,离线
    2.3.2 HttpSessionAttributeListener 监听器
    1. 作用:监听 Session 属性的变化
    2. 相关方法

    image-20231107160049663

    image-20231107160055551

    1. 使用少 , 使用方法和前面一样。
    2.3.3 ServletRequestListener 监听器

    1. ServletRequestListener 监听器

    2. 作用:监听 Request 创建或销毁,即 Request 生命周期监听

    相关方法

    image-20231107160125232

    1. 可以用来监控, 某个 IP 访问我们网站的频率, 日志记录 ,访问资源的情况.
    2.3.4 ServletRequestAttributeListener 监听器

    1. 作用:监听 Request 属性变化

    2. 相关方法

    image-20231107160152805

    image-20231107160159961

    1. 使用方法和前面类似
    2.3.5 HttpSessionBindingListener 感知监听器
    2.3.6 HttpSessionActivationListener 感知监听器

    七、JavaWeb 三大组件之 过滤器 Filter

    1. Filter 过滤器说明

    image-20231107172917453

    • 过滤器介绍

    **1. Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)

    1. Filter 过滤器是 JavaEE 的规范,是接口

    image-20231107173012940

    1. Filter 过滤器它的作用是:拦截请求,过滤响应。
    2. 应用场景
    • 权限检查
    • 日记操作
    • 事务管理

    2. Filter 过滤器基本原理

    image-20231107173102642

    3. Filter 应用案例

    D:\IDEA_code\xjz_javaweb\filter

    4. Filter 过滤器 url-pattern

    1、url-pattern : Filter 的拦截路径, 即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
    2.、精确匹配 /a.jsp 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截
    3、目录匹配 /manage/*对应的 请求地址http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截

    4、后缀名匹配 *.jsp 后缀名可变,比如 *.action *.do 等等对应的 请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截
    5、Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在

    5. Filter 过滤器生命周期

    • Filter 过滤器生命周期 图解

    image-20231107193710685

    6. FilterConfig

    • FilterConfig 接口图

    image-20231107193801088

    • FilterConfig 说明
    1. FilterConfig 是 Filter 过滤器的配置类
    2. Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
    3. FilterConfig 对象作用是获取 filter 过滤器的配置内容
    • 应用实例

    D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\XjzFilterConfig.java

    7. FilterChain 过滤器链

    • FilterChain 简介

    一句话: FilterChain: 在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链。

    • 基本原理试示意图

    image-20231107194837399

    • 应用实例

    D:\IDEA_code\xjz_javaweb\filter\src\com\xjz\filter\AFilter.java/BFilter.java

    • 运行结果

    image-20231108114655458

    • FilterChain 注意事项和细节
    1. 多个 filter 和目标资源在一次 http 请求,在同一个线程中

    2. 当一个请求 url 和 filter 的 url-pattern 匹配时, 才会被执行, 如果有多个匹配上,就会顺序执行,形成一个 filter 调用链(底层可以使用一个数据结构搞定)

    3. 多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象

    4. 多个 filter 执行顺序,和 web.xml 配置顺序保持一致.

    5. chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法, 如果后面没有过滤器,则执行目标资源。

    6. 小结:注意执行过滤器链时, 顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter() -> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 -> 返回给浏览器页面/数据

    八、数据交换和异步请求 - JSON & Ajax

    1. JSON 介绍

    1. JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)

    2. JSON 是轻量级的文本数据交换格式

    3. JSON 独立于语言 [老韩解读:即 java 、php、asp.net , go 等都可以使用 JSON]

    4. JSON 具有自我描述性,更易理解, 一句话,非常的好用…

    2. JSON 规则

    1. 映射(元素/属性)用冒号 : 表示,“名称”:值 , 注意名称是字符串,因此要用双引号引起来
    2. 并列的数据之间用逗号 , 分隔。“名称 1”:值,“名称 2”:值
    3. 映射的集合(对象)用大括号 {} 表示。{“名称 1”:值,“名称 2”:值}
    4. 并列数据的集合(数组)用方括号 [] 表示。 [{“名称 1”:值,“名称 2”:值}, {“名称 1”:值," 名称 2":值}]
    5. 元素值类型:string, number, object, array, true, false, null

    3. JSON 在 java 中使用

    1. 说明

    1. java 中使用 json,需要引入到第 3 方的包 gson.jar

    2. Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。

    3. 可以对 JSON 字符串 和 Java 对象相互转换

    2. JSON 在 java中 应用场景

    1. Javabean 对象和 json 字符串 的转换
    2. List 对象和 json 字符串 的转换
    3. map 对象和 json 字符串 的转换
    4. 应用场景示意图

    image-20231110190530434

    3. 应用实例 JSON 在 java中应用场景

    JavaJson.java

    package com.xjz.json;
    
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class JavaJson {
    
        //代码解读
        //1. 要把复杂的 json 字符串转换成为 java 对象。需要继承 TypeToken 类
        //2. TypeToken 是一个自定义泛型类,在创建时,需要指定具体类型,这里我们指定为 List
        // ,如果同学们忘记了,回去看 java 基础的泛型知识点
        //3. TypeToken 是由 gson 包提供的
        static class BookType extends TypeToken<List<Book>> {
        }
    
        public static void main(String[] args) {
    
            // new 一个 gson 对象。引入 gson 包
            Gson gson = new Gson();
    
            // java 对象和 json 的转换
            System.out.println("\n==== 1. java 对象 和 json 的转换 ====");
            Book book = new Book(100, "我爱学Java");
    
            //1. 演示把 javabean -> json字符串
            String strBook = gson.toJson(book);
            System.out.println("strBook= " + strBook);
            //2. json字符串 -> javabean
            //代码解读
            //(1) strBook 就是 json字符串
            //(2) Book.class 指定将 json字符串转成 Book对象
            //(3) 底层是反射机制
            Book book2 = gson.fromJson(strBook, Book.class);
            System.out.println("book2= " + book2);
    
            //2. List 集合 和 json 的转换
            System.out.println("\n==== 2. List 集合 和 json 的转换 ====");
            List<Book> bookList = new ArrayList<>();
            bookList.add(new Book(200, "男人帮"));
            bookList.add(new Book(300, "女人帮"));
            // 将 list 转成 json对象
            String bookListStr = gson.toJson(bookList);
            System.out.println("bookListStr= " + bookListStr);
    
            //将 json 字符串 转成 List 集合方式 1
            Type type = new BookType().getType();
            System.out.println("type= " + type); // java.util.List
            List<Book> bookList2 = gson.fromJson(bookListStr, type);
            System.out.println("bookList2= " + bookList2);
    
            //将 json 字符串 转成 List 集合方式 2
            //代码解读
            //(1) 如果我们 new TypeToken>() 提示
            //  'TypeToken()' has protected access in 'com.google.gson.reflect.TypeToken'
            //(2) 因为 TypeToken 的无参构造器是 protected , 而 new TypeToken>() 就是调用其无参构造器
            //(3) 根据java基础,如果一个方法是 protected,而且不在同一个包中,是不能直接访问的,因此报错
            //(4) 为什么 new TypeToken>(){} 使用就可以,这里就涉及到 匿名内部类的知识点.
            //(5) 当 new TypeToken>(){} 其实这个类型不是 TypeToken,而是一个 匿名内部类(可以理解为TypeToken的子类)
            //(6) 而且这个匿名内部类是有自己的无参构造器(隐式),根据java基础规则:当执行子类的无参构造器时,默认super()
            //Type type = new TypeToken>(){}.getType(); // java.util.List
    
            List<Book> bookList3 = gson.fromJson(bookListStr, new TypeToken<List<Book>>() {}.getType());
            System.out.println("bookList3= " + bookList3);
    
            //3.  map 集合 -> json 字符串
            Map<String, Book> bookMap = new HashMap<>();
            bookMap.put("k1",new Book(10,"三国演义"));
            bookMap.put("k2",new Book(20,"水浒传"));
    
            // 把 map 集合 -> json 字符串
            String strBookMap = gson.toJson(bookMap);
            System.out.println("strBookMap= " + strBookMap + "类型 = "+strBookMap.getClass());
    
            // 把 json 字符串 -> map 集合
            // new TypeToken>() {}.getType() => java.util.Map
            Map<String, Book> bookMap2 = gson.fromJson(strBookMap,
                    new TypeToken<Map<String, Book>>() {
                    }.getType());
            System.out.println("bookMap2= " + bookMap2);
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    九、Ajax 基本介绍

    1. Ajax 是什么

    1. AJAX 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML)
    2. Ajax 是一种浏览器异步发起请求(指定发哪些数据),局部更新页面的技术

    2. Ajax 经典应用场景

    1. 搜索引擎根据用户输入关键字,自动提示检索关键字
    2. 动态加载数据,按需取得数据【树形菜单、联动菜单…】
    3. 改善用户体验。【输入内容前提示、带进度条文件上传…】
    4. 电子商务应用。 【购物车、邮件订阅…】
    5. 访问第三方服务。【访问搜索服务、rss 阅读器】
    6. 页面局部刷新, https://piaofang.maoyan.com/dashboard

    3. Ajax 原理示意图

    • 传统的WEB应用

    image-20231111160818070

    • Ajax 原理示意图

    image-20231111160934359

    4. JavaScript 原生 Ajax 请求

    在线文档:https://www.w3school.com.cn/js/js_ajax_intro.asp

    • 应用实例-验证用户名是否存在

    演示 javascript 发送原生 ajax 请求的案例

    1. 在输入框输入用户名

    2. 点击验证用户名, 使用 ajax 方式, 服务端验证该用户名是否已经占用了, 如果该用户
      已经占用, 以 json 格式返回该用户信息

    3. 假定用户名为 king , 就不可用, 其它用户名可以=》 后面我们接入 DB[Mysql+JDBC]

    4. 对页面进行局部刷新, 显示返回信息

    5. 小思考: 为什么直接返回用户名是否可用信息, 完成案例后, 再思考?

    image-20231111170858606

    image-20231111170914202

    十、线程数据共享和安全 ThreadLocal

    1. 什么是 ThreadLocal

    1. ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
    2. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]
    3. ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法
    4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例
    5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
    6. ThreadLocal 中保存数据,在线程销毁后,会自动释放

    image-20231112181826158

    2. 快速人门 ThreadLocal

    ThreadLocalTest.java

    package com.xjz.threadlocal;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class ThreadLocalTest {
    
        public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
        public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
    
    
        public static class Task implements Runnable{
    
            @Override
            public void run() {
                Dog dog = new Dog();
                Pig pig = new Pig();
                /**
                 * ===== set源码分析 只要明白这个机制,后面的 set get 全部通透=======
                 *
                 * public void set(T value) {
                 *          //获取当前线程
                 *         Thread t = Thread.currentThread();
                 *         //获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals,
                 *         //  ,类型是 ThreadLocal 的静态内部类
                 *         //threadLocals 有一个属性 Entry[],类型 }ThreadLocal.ThreadLocalMap.Entry
                 *         // k-> ThreadLocal 对象  v -> 值
                 *         ThreadLocalMap map = getMap(t);
                 *         if (map != null)
                 *             map.set(this, value); //存放这里的 this 就是 ThreadLocal1,可以 debug源码,一目了然
                 *         else
                 *             createMap(t, value); //创建
                 *     }
                 *
                 *     =======  getMap 方法源码======
                 *     ThreadLocalMap getMap(Thread t) { //获取当前线程的 ThreadLocal.ThreadLocalMap
                 *         return t.threadLocals;
                 *     }
                 *
                 *     说明:
                 *     1. ThreadLocalMap 对象对象是和当前 Thread对象的绑定属性
                 *     2. ThreadLocalMap 对象含有 Entry[] table; 这个 Entry(K,V)
                 *     3. 这个 key 就是 ThreadLocal 对象, V 就是你要在放入的对象,比如 dog
                 *     4. 当执行了 了 threadLocal.set(dog) 后,内存布局图为 wps[看图]
                 *
                 */
                threadLocal1.set(dog);
                threadLocal2.set(pig);// 会替换 dog
                //如果希望在同一个线程共享多个对象/数据,就只能再创建一个 ThreadLocal 对象
                //threadLocal2.set(pig);
                System.out.println("在 run方法中 线程 name=" + Thread.currentThread().getName()
                        + " 放入 ThreadLocal 的数据= " +  dog );
                new T1Service().update();
            }
        }
        public static void main(String[] args) {
    
            for (int i = 0; i < 1; i++) {
                new Thread(new Task()).start();//启动一个新的线程,注意不是主线程
            }
            System.out.println("在 main 方法中 ThreadLocal 的数据=" + threadLocal1.get());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    T1Service.java

    package com.xjz.threadlocal;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class T1Service {
        public void update(){
            String name = Thread.currentThread().getName();
            /**
             * ====== ThreadLocalTest.threadLocal.get() 源码====
             *  public T get() {
             *         Thread t = Thread.currentThread();
             *         ThreadLocalMap map = getMap(t);
             *         if (map != null) {
             *             ThreadLocalMap.Entry e = map.getEntry(this);
             *             if (e != null) {
             *                 @SuppressWarnings("unchecked")
             *                 T result = (T)e.value;
             *                 return result;
             *             }
             *         }
             *         return setInitialValue();
             *     }
             *
             *     代码解读
             *     1. 先得到当前线程对象
             *     2. 获取当前对象绑定的 ThreadLocalMap 对象
             *     3. 根据 threadLocal对象(key)获取 ThreadLocalMap 对象的 Entry数组的 Entry对象 [hash]
             *     4. 取出 Entry对象的 value 就是放入到 ThreadLocal对象的数据 dog
             *
             */
            System.out.println("在 T1Service 的 update() 线程 name=" +
                    name + "threadLocal 的数据= " + ThreadLocalTest.threadLocal1.get());
            System.out.println("在 T1Service 的 update() 线程 name=" +
                    name + "threadLocal 的数据= " + ThreadLocalTest.threadLocal2.get());
            new T2DAO().update();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    T2DAO.java

    package com.xjz.threadlocal;
    
    /**
     * @author xjz_2002
     * @version 1.0
     */
    public class T2DAO {
    
        public void update(){
            String name = Thread.currentThread().getName();
            System.out.println("在 T2DAO 的 update() 线程是=" + name
                + "threadlocal 数据是=" + ThreadLocalTest.threadLocal1.get());
            System.out.println("在 T2DAO 的 update() 线程是=" + name
                    + "threadlocal 数据是=" + ThreadLocalTest.threadLocal2.get());
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6. 完成测试, 检测 ThreadLocal 可以实现在同一个线程数据安全共享

    image-20231112212321404

    3. ThreadLocal 源码解读+画图

    1. ThreadLocal 原理分析图(重点 set 和 get)

    1. Debug 源码图,非常重要

    image-20231112212645317

  • 相关阅读:
    29【定时器和延时器】
    【Harmony OS】【ArkUI】ets开发 简易视频播放器
    【Java】BMI身体质量指数计算工具
    Vue面试题以及解答(持续扩展中.....)
    【国庆头像】来一波美女国庆头像 超好看
    Mysql5.7安装配置详细图文教程(msi版本)
    VL (Vision and Language) 任务简介及数据集
    30、ES集成到项目中
    # 我实践:用docker registry API 获取清单并删除某仓库某tag镜像
    9 种方法使用 Amazon CodeWhisperer 快速构建应用
  • 原文地址:https://blog.csdn.net/m0_53125903/article/details/134472236