JavaWeb基础(3)-会话技术(cookie和session),过滤器(Filter),监听器(Listener)

JavaWeb基础(3)-会话技术(cookie和session),过滤器(Filter),监听器(Listener)

文章目录

  • JavaWeb基础(3)-会话技术(cookie和session),过滤器(Filter),监听器(Listener)
    • 8 会话技术(Cookie、Session)
      • 8.1 会话与会话跟踪技术
        • 8.1.1 会话
        • 8.1.2 会话跟踪
      • 8.2 Cookie
        • 8.2.1 Cookie的基本使用
          • 8.2.1.1 服务端发送Cookie
          • 8.2.1.2 服务端获取Cookie
        • 8.2.2 cookie案例
        • 8.2.3 cookie使用细节
      • 8.3 Session
        • 8.3.1 Session的基本使用
        • 8.3.2 Session原理
        • 8.3.3 Session使用细节
          • 8.3.3.1 Session钝化与活化
          • 8.3.3.2 Session销毁
      • 8.4 总结
    • 9 Filter(过滤器)、Listener(监听器)
      • 9.1 Filter过滤器
        • 9.1.2 Filter快速入门
        • 9.1.3 Filter执行流程
        • 9.1.4 Filter拦截路径配置
        • 9.1.5 过滤器链
        • 9.1.6 放行
      • 9.2 Listener监听器
        • 9.2.1 监听器分类
        • 9.2.2 监听器运用
        • 9.2.2 监听器运用

8 会话技术(Cookie、Session)

8.1 会话与会话跟踪技术

8.1.1 会话

会话: 用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。

  • 在一次会话中可以包含多次请求和响应
  • 浏览器发出请求到服务端响应数据给前端之后,一次会话(在浏览器和服务器之间)就被建立了
  • 会话被建立后,如果浏览器或服务端都没有被关闭,则会话就会持续建立着
  • 浏览器和服务器就可以继续使用该会话进行请求发送和响应,上述的整个过程就被称之为会话

用实际场景来理解下会话,比如在我们访问京东的时候,当打开浏览器进入京东首页后,浏览器和京东的服务器之间就建立了一次会话,后面的搜索商品,查看商品的详情,加入购物车等都是在这一次会话中完成。

思考:下图中总共建立了几个会话?

image-20240102141637364

每个浏览器都会与服务端建立了一个会话,加起来总共是3个会话。

8.1.2 会话跟踪

引入:

  • HTTP协议是无状态的,靠HTTP协议是无法实现会话跟踪
  • 想要实现会话跟踪,就需要用到Cookie和Session

会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

  • 服务器需要用来识别请求是否来自同一个浏览器
  • 服务器用来识别浏览器的过程,这个过程就是会话跟踪;
  • 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据

为什么现在浏览器和服务器不支持数据共享

  • 浏览器和服务器之间使用的是HTTP请求来进行数据传输
  • HTTP协议是无状态的,(之前学的状态是响应状态码,在请求数据时不起作用)每次浏览器向服务器请求时,服务器都会将该请求视为新的请求
  • HTTP协议设计成无状态的目的是让每次请求之间相互独立,互不影响
  • 请求与请求之间独立后,就无法实现多次请求之间的数据共享

一个会话中的多次请求需要共享数据的情况:

  • 加入购物车去购物车结算是两次请求,但是后面这次请求要想展示前一次请求所添加的商品,就需要用到数据共享

  • 页面展示用户登录信息:很多网站,登录后访问多个功能发送多次请求后,浏览器上都会有当前登录用户的信息(登陆后在不同页面跳转任可以看到自己的信息)

  • 网站登录页面的【记住我】功能:通常是用于控制是否在用户的浏览器中设置一个长期有效的 Cookie

    注意:网页自动登录的背后原理中,会话技术中的cookie或其他相关技术并没有直接存储账号和密码,Cookie本身并不存储账号和密码,而是包含一些用于识别用户身份的信息的小型文本文件。

    在网站上实现**自动登录**的过程通常如下:

    1. 登录过程: 用户在登录页面输入用户名和密码,服务器验证这些信息,如果验证成功,会为用户创建一个会话。
    2. 会话管理: 服务器会将一个唯一的标识符(通常是一个会话 ID)发送到用户的浏览器,该标识符用于标识用户的会话。
    3. Cookie: 服务器通常使用 Cookie 将会话标识符存储在用户的浏览器中。Cookie 是存储在用户计算机上的小型文本文件,它包含有关用户的信息,例如会话标识符、过期时间等。
    4. 下次访问: 当用户再次访问网站时,浏览器会将存储的 Cookie 发送回服务器,服务器通过解析 Cookie 中的会话标识符来识别用户。如果会话仍然有效(未过期),用户就被视为已经登录。
    5. 自动登录: 如果用户的会话仍然有效,服务器可以自动将用户登录,而无需再次输入用户名和密码。

    当用户修改其账号对应的密码时,服务器会采取相应的措施来保障安全性,而与之相关的会话可能会被标记为无效或过期

  • 登录页面的验证码功能:生成验证码和输入验证码点击注册这也是两次请求,这两次请求的数据之间要进行对比,相同则允许注册

实现会话跟踪技术的两种方式:

(1) 客户端会话跟踪技术:Cookie

(2) 服务端会话跟踪技术:Session

这两个技术都可以实现会话跟踪,它们之间最大的区别:Cookie是存储在浏览器端而Session是存储在服务器

8.2 Cookie

概念:客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问

image-20240102155936268

  • 服务端提供了两个Servlet,分别是ServletA和ServletB
  • 浏览器发送HTTP请求1给服务端,服务端ServletA接收请求并进行业务处理
  • **服务端ServletA在处理的过程中可以创建一个Cookie对象**并将name=zs的数据存入Cookie
  • 服务端ServletA在响应数据的时候,会把Cookie对象响应给浏览器
  • 浏览器接收到响应数据,会把Cookie对象中的数据存储在浏览器内存中,此时浏览器和服务端就建立了一次会话
  • 在同一次会话中浏览器再次发送HTTP请求2给服务端ServletB,浏览器会携带Cookie对象中的所有数据
  • ServletB接收到请求和数据后,就可以获取到存储在Cookie对象中的数据,这样同一个会话中的多次请求之间就实现了数据共享
8.2.1 Cookie的基本使用
8.2.1.1 服务端发送Cookie
  • 创建Cookie对象,并设置数据
Cookie cookie = new Cookie("key","value");
  • 发送Cookie到客户端:使用response对象
response.addCookie(cookie);
  • 查看浏览器中的cookie

    image-20240102202013577

    image-20240102202101298

    image-20240102202227209

    在这里可以看到不同网站下的cookie

    image-20240102202330475

    另一种查看方式:使用开发者工具

    image-20240102202602214

8.2.1.2 服务端获取Cookie
  • 获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
  • 遍历数组,获取每一个Cookie对象:for
  • 使用Cookie对象方法获取数据(这两个方法不能传入参数
cookie.getName();
cookie.getValue();
  • Spring框架中注解:@CookieValue

    @CookieValue 注解用于从HTTP请求中获取指定名称的 Cookie 的值。

    import org.springframework.web.bind.annotation.CookieValue;
    import org.springframework.web.bind.annotation.GetMapping;
    
    // ...
    
    @GetMapping("/example")
    public String exampleMethod(@CookieValue(name = "yourCookieName", required = false) String yourCookieValue) {
        // 在这里你可以使用yourCookieValue来处理Cookie的值
        System.out.println("Your Cookie Value: " + yourCookieValue);
    
        // 其他业务逻辑...
    
        return "yourView";
    }
    

    @CookieValue注解允许你通过方法参数直接获取指定name的Cookie值。请注意,required = false表示如果找不到指定的Cookie也不会抛出异常,而是将yourCookieValue设置为null

8.2.2 cookie案例
  • 编写Servlet类,名称为AServlet

  • 在AServlet中创建Cookie对象,存入数据,发送给前端

  • 启动测试,在浏览器查看Cookie对象中的值

  • 编写一个新Servlet类,名称为BServlet

  • 在BServlet中使用request对象获取Cookie数组,遍历数组,从数据中获取指定名称对应的值

  • 启动测试,在控制台打印出获取的值

代码:

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //发送Cookie
        //1. 创建Cookie对象
        Cookie cookie = new Cookie("username","zs");
        //2. 发送Cookie,response
        response.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
@WebServlet("/bServlet")
public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Cookie
        //1. 获取Cookie数组
        Cookie[] cookies = request.getCookies();
        //2. 遍历数组
        for (Cookie cookie : cookies) {
            //3. 获取数据
            String name = cookie.getName();
            if("username".equals(name)){
                String value = cookie.getValue();
                System.out.println(name+":"+value);
                break;
            }
        }

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
8.2.3 cookie使用细节
  • 默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁

  • 设置Cookie存活时间setMaxAge(int seconds)

    1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除

    2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁

    3.零:删除对应Cookie

    //发送Cookie
    //1. 创建Cookie对象
    Cookie cookie = new Cookie("username","zs");
    //设置存活时间   ,1周 7天
    cookie.setMaxAge(60*60*24*7); //易阅读,需程序计算
    //cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算
    //2. 发送Cookie,response
    response.addCookie(cookie);
    
  • Cookie不能直接存储中文(无论是name还是value)

    解决方法:在Servlet中对中文进行URL编码,采用URLEncoder.encode(),将编码后的值存入Cookie中

    //发送Cookie
    String value = "张三";
    //对中文进行URL编码
    value = URLEncoder.encode(value, "UTF-8");
    //将编码后的值存入Cookie中
    Cookie cookie = new Cookie("username",value);
    //设置存活时间   ,1周 7天
    cookie.setMaxAge(60*60*24*7);
    //2. 发送Cookie,response
    response.addCookie(cookie);
    

8.3 Session

Session:服务端会话跟踪技术:将数据保存到服务端。

  • Session是存储在服务端;而Cookie是存储在客户端
  • 存储在客户端的数据容易被窃取和截获,存在很多不安全的因素
  • 存储在服务端的数据相比于客户端来说就更安全

image-20240102205323844

  • 在服务端的AServlet获取一个Session对象,把数据存入其中
  • 在服务端的BServlet获取到相同的Session对象,从中取出数据
  • 就可以实现一次会话中多次请求之间的数据共享
8.3.1 Session的基本使用
  • HttpSession session = request.getSession(); 获取Session对象,使用的是request对象

  • Session对象提供的功能:

    • void setAttribute(String name, Object o);存储数据到 session 域中;Session中可以存储的是一个Object类型的数据,也就是说Session中可以存储任意数据类型。

    • Object getAttribute(String name);根据 key,获取值

    • void removeAttribute(String name);根据 key,删除该键值对

8.3.2 Session原理

Session是如何保证在一次会话中获取的Session对象是同一个呢?

Session是基于Cookie实现的

image-20240102210604619

请求1、2的session对象是同一个;但是请求3的session对象并不是

Session背后的原理:Session是基于Cookie实现的:

(1)demo1在第一次获取session对象的时候,session对象会有一个唯一的标识,假如是id:10

(2)demo1在session中存入其他数据并处理完成所有业务后,需要通过Tomcat服务器响应结果给浏览器

(3)Tomcat服务器发现业务处理中使用了session对象,就会把session的唯一标识id:10当做一个cookie,添加Set-Cookie:JESSIONID=10到响应头中,并响应给浏览器

(4)浏览器接收到响应结果后,会把响应头中的coookie数据存储到浏览器的内存中

(5)浏览器在同一会话中访问demo2的时候,会把cookie中的数据按照cookie: JESSIONID=10的格式添加到请求头中并发送给服务器Tomcat

(6)demo2获取到请求后,从请求头中就读取cookie中的JSESSIONID值为10,然后就会到服务器内存中寻找id:10的session对象,如果找到了,就直接返回该对象,如果没有则新创建一个session对象

(7)关闭打开浏览器后,因为浏览器的cookie已被销毁,所以就没有JESSIONID的数据,服务端获取到的session就是一个全新的session对象

image-20240102211049037
8.3.3 Session使用细节
8.3.3.1 Session钝化与活化

服务器重启后,Session中的数据是否还在?

只要服务器是正常关闭和启动,session中的数据是可以被保存下来的,但session对象也是一个新的对象

钝化:在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中

钝化的数据路径为:项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser

活化:再次启动服务器后,从文件中加载数据到Session中

数据加载到Session中后,路径中的SESSIONS.ser文件会被删除掉

要实现 Session 数据的共享,需要确保用户的浏览器在一段时间内保持开启状态。

8.3.3.2 Session销毁

session的销毁会有两种方式:

  • 默认情况下,无操作,30分钟自动销毁

    对于这个失效时间,是可以通过配置进行修改的

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
    
        <session-config>
            <session-timeout>100</session-timeout>
        </session-config>
    </web-app>
    

    如果没有配置,默认是30分钟,默认值是在Tomcat的web.xml配置文件中写死的

    image-20240102213221768

  • 调用Session对象的invalidate()进行销毁

    // 销毁
    session.invalidate();
    

8.4 总结

Cookie 和 Session 都是来完成一次会话内多次请求间数据共享的。

所需两个对象放在一块,就需要思考:

Cookie和Session的区别是什么?

Cookie和Session的应用场景分别是什么?

  • 区别:
    • 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
    • 安全性:Cookie不安全,Session安全
    • 数据大小:Cookie最大3KB,Session无大小限制
    • 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
    • 服务器性能:Cookie不占服务器资源,Session占用服务器资源
  • 应用场景:
    • 购物车:使用Cookie来存储
    • 以登录用户的名称展示:使用Session来存储(登录之后多个页面之间都可以看到自己的信息,不用反复登录)
    • **记住我功能:**使用Cookie来存储
    • 验证码:使用session来存储
  • 结论
    • Cookie是用来保证用户在**未登录情况下的身份识别**
    • Session是用来保存用户**登录后的数据**

介绍完Cookie和Session以后,具体用哪个还是需要根据具体的业务进行具体分析。

9 Filter(过滤器)、Listener(监听器)

Filter 表示过滤器,Listener 表示监听器,他们属于 JavaWeb 三大组件(Servlet、Filter、Listener)

9.1 Filter过滤器

过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。

如下图所示,浏览器可以访问服务器上的所有的资源(servlet、jsp、html等)

而在访问到这些资源之前可以使过滤器拦截来下,也就是说在访问资源之前会先经过 Filter,如下图

image-20210823184657328

拦截器拦截到后可以做什么功能呢?

过滤器一般完成一些通用的操作。

比如每个资源都要写一些代码完成某个功能,我们总不能在每个资源中写这样的代码吧,而此时我们可以将这些代码写在过滤器中,因为**请求每一个资源都要经过过滤器**。

权限控制:在数据访问时,希望实现的效果是用户如果登陆过了就跳转到品牌数据展示的页面;如果没有登陆就跳转到登陆页面让用户进行登陆

细粒度权限控制:…

统一编码处理:…

敏感字符处理:…

9.1.2 Filter快速入门

进行 Filter 开发分成以下三步实现

  • 定义类,实现 Filter接口,并重写其所有方法

    我们将 filter 创建在 com.baidu.web.filter 包下

    image-20240103162719660

    import javax.servlet.*;
    import java.io.IOException;
    
    public class FilterDemo implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            
        }
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
        }
        @Override
        public void destroy() {
    
        }
    }
    
  • 配置Filter拦截资源的路径:在类上定义 @WebFilter 注解。而注解的 value 属性值 /* 表示拦截所有的资源

    image-20240103214805645

  • 在doFilter方法中输出一句话,并放行

    @WebFilter("/*")
    public class FilterDemo implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("this filter is running...");
            filterChain.doFilter(servletRequest,servletResponse);
        }
        @Override
        public void destroy() {
        }
    }
    

    运行结果:

    image-20240103214920673

    上述代码中的 filterChain.doFilter(request,response); 就是放行,也就是让其访问本该访问的资源。

9.1.3 Filter执行流程

image-20240103215836606

Filter的执行流程如下:

image-20240103215905964

执行流程测试:

后端:

image-20240103215941671

前端:

image-20240103215954182

(实验中这里JSP输出代码有误,不能在控制台输出)

输出结果:

image-20240103221600333

以后我们可以将对请求进行处理的代码放在放行之前进行处理,而如果请求完资源后还要对响应的数据进行处理时可以在放行后进行逻辑处理。

9.1.4 Filter拦截路径配置

Filter 会对请求的哪些资源进行拦截

使用 @WebFilter 注解进行配置。如:@WebFilter("拦截路径")

拦截路径有如下四种配置方式

  • 拦截具体的资源:/index.jsp:只有访问index.jsp时才会被拦截
  • 目录拦截:/user/*:访问/user下的所有资源,都会被拦截
  • 后缀名拦截:*.jsp:访问后缀名为jsp的资源,都会被拦截
  • 拦截所有:/*:访问所有资源,都会被拦截

通过上面拦截路径的学习,大家会发现拦截路径的配置方式和 Servlet 的请求资源路径配置方式一样,但是表示的含义不同。

9.1.5 过滤器链

过滤器链是指在一个Web应用,可以配置多个过滤器这多个过滤器称为过滤器链

image-20240103223841201

上图中的过滤器链执行是按照以下流程执行:(放行代码≠后逻辑代码)

  1. 执行 Filter1 的放行前逻辑代码
  2. 执行 Filter1放行代码
  3. 执行 Filter2 的放行前逻辑代码
  4. 执行 Filter2放行代码
  5. 访问到资源
  6. 执行 Filter2 的放行后逻辑代码
  7. 执行 Filter1 的放行后逻辑代码

以上流程串起来就像一条链子,故称之为过滤器链。

多个过滤器如何定义他的执行顺序呢?

我们现在使用的是注解配置Filter,而这种配置方式的优先级是按照过滤器类名(字符串)的自然排序。

比如有如下两个名称的过滤器 : BFilterDemoAFilterDemo 。那一定是 AFilterDemo 过滤器先执行。

9.1.6 放行

常见的情况:如果没有登录,就要跳转到登录页面,但是一些资源却被拦截了下来,如css渲染文件;同样的,如果没有账号需要注册,也要跳转到相关的注册页面,那么这个注册页面是需要被放行的。

我们要在判断用户是否登录之前(即相关session代码之前),对一些资源放行:

//判断访问资源路径是否和登录注册相关
//1,在数组中存储登陆和注册相关的资源路径
String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
//2,获取当前访问的资源路径
String url = req.getRequestURL().toString(); 

//3,遍历数组,获取到每一个需要放行的资源路径
for (String u : urls) {
    //4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串
    /*
    	比如当前访问的资源路径是  /brand-demo/login.jsp
    	而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行
    */
    if(url.contains(u)){
        //找到了,放行
        chain.doFilter(request, response);
        //break;
        return;
    }
}

那么这样过滤器的完整代码为:

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;
        
        //判断访问资源路径是否和登录注册相关
        //1,在数组中存储登陆和注册相关的资源路径
        String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
        //2,获取当前访问的资源路径
        String url = req.getRequestURL().toString(); 

        //3,遍历数组,获取到每一个需要放行的资源路径
        for (String u : urls) {
            //4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串
            /*
                比如当前访问的资源路径是  /brand-demo/login.jsp
                而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行
            */
            if(url.contains(u)){
                //找到了,放行
                chain.doFilter(request, response);
                //break;
                return;
            }
        }
   
        //1. 判断session中是否有user
        HttpSession session = req.getSession();
        Object user = session.getAttribute("user");

        //2. 判断user是否为null
        if(user != null){
            // 登录过了
            //放行
            chain.doFilter(request, response);
        }else {
            // 没有登陆,存储提示信息,跳转到登录页面

            req.setAttribute("login_msg","您尚未登陆!");
            req.getRequestDispatcher("/login.jsp").forward(req,response);
        }
    }

    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }
}

9.2 Listener监听器

  • Listener 表示监听器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一
  • 监听器可以监听就是在 application,session,request 三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件
  • applicationServletContext 类型的对象
  • ServletContext 代表整个web应用在服务器启动的时候,tomcat会自动创建该对象在服务器关闭时会自动销毁该对象
9.2.1 监听器分类

JavaWeb 提供了8个监听器:

image-20240103225131071

常用: ServletContextListener

ServletContextListener 是用来监听 ServletContext 对象的创建和销毁

ServletContextListener 接口中有以下两个方法:

  • void contextInitialized(ServletContextEvent sce)ServletContext 对象**被创建**了会自动执行的方法
  • void contextDestroyed(ServletContextEvent sce)ServletContext 对象**被销毁**时会自动执行的方法
9.2.2 监听器运用

ServletContextListener 监听器

  • 定义一个类,实现ServletContextListener 接口
  • 重写所有的抽象方法
  • 使用 @WebListener 进行配置

代码如下:

@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //加载资源
        System.out.println("ContextLoaderListener...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //释放资源
    }
}

启动服务器,就可以在启动的日志信息中看到 contextInitialized() 方法输出的内容,同时也说明了 ServletContext 对象在服务器启动的时候被创建了。

运行结果:

image-20240103225721047

9.2.2 监听器运用

ServletContextListener 监听器

  • 定义一个类,实现ServletContextListener 接口
  • 重写所有的抽象方法
  • 使用 @WebListener 进行配置

代码如下:

@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //加载资源
        System.out.println("ContextLoaderListener...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //释放资源
    }
}

启动服务器,就可以在启动的日志信息中看到 contextInitialized() 方法输出的内容,同时也说明了 ServletContext 对象在服务器启动的时候被创建了。

运行结果:

image-20240103225721047

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/294939.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【28】Kotlin语法进阶——使用协程编写高效的并发程序

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、Kotlin中的协程1.1 协程的基本用法1.1.1协程与协程作用域1.1.2 使用launch函数创建子协程1.1.3 通过suspend关键声明挂起函数1.1.4 coroutineScope函数 1.2…

Spark调优解析-sparkshuffle和程序开发优化2(七)

1Shuffle调优 1.1调优概述 大多数Spark作业的性能主要就是消耗在了shuffle环节&#xff0c;因为该环节包含了大量的磁盘IO、序列化、网络数据传输等操作。因此&#xff0c;如果要让作业的性能更上一层楼&#xff0c;就有必要对shuffle过程进行调优。但是也必须提醒大家的是&a…

预判了预判,结局接受失败——2023年度总结

预判了预判&#xff0c;结局接受失败 引言一整年&#xff0c;你都在干什么活该解释我炒股前的上一份工作如何走上炒股之路计划的失败来得是那么干脆成功失败盈亏比失败预感与到来 失败的后果开始侵蚀得到失去 重新启程AI带来的改变当下的机会 新年Flag方向 总结 引言 时间过得…

【c++】入门3

引用 1.swap交换两个变量值的时候可以用引用 2.例题中通过前序遍历数组构建二叉树&#xff0c;可以用引用传别名. #include <stdio.h> #include <stdlib.h> typedef struct BinaryTreeNode {char data;struct BinaryTreeNode* left;struct BinaryTreeNode* right; …

gz-hamonic 安装提示缺少许多依赖无法安装

在软件更新源中增加gz-hamonic的软件源&#xff0c; 点击添加&#xff0c;在输入框中填入如下语句&#xff1a; deb http://packages.osrfoundation.org/ubuntu jammy main 如图所示&#xff1a; 然后执行 sudo apt -get install gz-hamonic即可安装。 如下图 在终端中输入…

FFmpeg调用MediaCodec解码

在前面的博文中我们介绍了关于使用NDK编译FFMpeg6.0的一些坑以及相关的解决方法。 详情请参考&#xff1a;NDK编译ffmpeg6.0与x264的坑 在写《NDK编译ffmpeg6.0与x264的坑》一文的时候就说过了&#xff0c;我们编译FFmpeg6.0的目的就是为了体验一下它NDK式的MediaCodec硬解码…

密码学:一文看懂初等数据加密一对称加密算法

文章目录 对称加密算法简述对称加密算法的由来对称加密算法的家谱数据加密标准-DES简述DES算法的消息传递模型DES算法的消息传递过程和Base64算法的消息传递模型的区别 算法的实现三重DES-DESede三重DES-DESede实现 高级数据加密标准一AES实现 国际数据加密标准-IDEA实现 基于口…

启发式算法解决TSP、0/1背包和电路板问题

1. Las Vegas 题目 设计一个 Las Vegas 随机算法&#xff0c;求解电路板布线问题。将该算法与分支限界算法结合&#xff0c;观察求解效率。 代码 python代码如下&#xff1a; # -*- coding: utf-8 -*- """ Date : 2024/1/4 Time : 16:21 Author : …

Java爬虫获取省市区镇村5级行政区划

公司有个项目需要五级行政区划,没有现成的数据,写了一段代码,从gj统计j获取的数据。记录一下。 1.引入maven解析html <!-- jsoup --> <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.3&…

微信小程序自动化测试实战,支持录制回放、智能遍历

​为了满足小程序性能、功能等方面的测试需求&#xff0c;微信团队上线 小程序云测服务&#xff0c;提供丰富的自动化测试能力。其中 智能化 Monkey 服务 凭借着零代码、低成本的优势吸引不少开发者使用。 在服务使用过程中&#xff0c;我们发现开发者有更多的进阶需求&#x…

OAI openair3代码结构整理

openair3代码框架结构 OAI&#xff08;OpenAirInterface&#xff09;是一个开源的5G网络软件平台&#xff0c;用于研究和开发5G网络技术。OpenAir3是OAI项目中的一个子项目&#xff0c;专注于5G核心网络的功能实现。 一、OpenAir3的代码主要包括以下几个部分&#xff1a; NAS…

Proxmox VE 8 安装开源监控平台Centreon 23

作者&#xff1a;田逸&#xff08;formyz&#xff09; 非常好用的开源监控系统Centreon从版本号21.40以后&#xff08;包括Centreon 21.40这个版本&#xff09;&#xff0c;不在提供ISO一键式安装包&#xff0c;取而代之的是在线脚本安装和VMware虚拟机或者Oracle VirtualBox 虚…

初识MySQL

一、什么是数据库 数据库&#xff08;Database&#xff0c;简称DB&#xff09;&#xff1a;长期存放在计算机内&#xff0c;有组织、可共享的大量数据的集合&#xff0c;是一个数据“仓库”。 数据库的作用&#xff1a; 可以结构化存储大量的数据&#xff0c;方便检索和访问…

2024最新Java基础面试题大全(一)

1、String可以被继承&#xff1f; 不能被继承&#xff0c;因为String类有final修饰符&#xff0c;而final修饰的类是不能被继承的。 public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// 省略...  }2、常见集合类 Java…

ChatGPT到底能做什么呢?

1、熟练掌握ChatGPT提示词技巧及各种应用方法&#xff0c;并成为工作中的助手。 2、通过案例掌握ChatGPT撰写、修改论文及工作报告&#xff0c;提供写作能力及优化工作 3、熟练掌握ChatGPT融合相关插件的应用&#xff0c;完成数据分析、编程以及深度学习等相关科研项目。 4、…

深入理解Vue3中的自定义指令

Vue3是一个流行的前端框架&#xff0c;它引入了许多新特性和改进&#xff0c;其中之一是自定义指令。自定义指令是一种强大的功能&#xff0c;可以让开发者在模板中直接操作 DOM 元素。本文将深入探讨 Vue3中的自定义指令&#xff0c;包括自定义指令的基本用法、生命周期钩子函…

【fiddler】fiddler抓包工具的使用

前言&#xff1a;我们可以通过fiddler软件&#xff0c;捕获到http请求&#xff0c;并修改请求参数 修改返回内容 fiddler下载,官网如下图 启动fiddler软件,点击file 选择 Capture Traffic 修改入参 (我们以谷歌浏览器发起请求为例) 此时会出现一个向上的箭头&#xff0c;点击…

MediaPipeUnityPlugin Win10环境搭建(22年3月的记录,新版本已完全不同,这里只做记录)

https://github.com/homuler/MediaPipeUnityPlugin You cannot build libraries for Android with the following steps. 1、安装msys2配置系统环境变量Path添加 C:\msys64\usr\bin 执行 pacman -Su 执行 pacman -S git patch unzip 2、安装Python3.9.10 勾选系统环境变量 …

stm32学习总结:6、Proteus8+STM32CubeMX+MDK仿真蜂鸣器及ADC读取电压(Proteus标签整理原理图)

stm32学习总结&#xff1a;6、Proteus8STM32CubeMXMDK仿真蜂鸣器及ADC读取电压&#xff08;Proteus标签整理原理图&#xff09; 文章目录 stm32学习总结&#xff1a;6、Proteus8STM32CubeMXMDK仿真蜂鸣器及ADC读取电压&#xff08;Proteus标签整理原理图&#xff09;一、前言二…

智能革命:揭秘AI如何重塑创新与效率的未来

1.AI技术的发展与应用 1.1 AI技术的发展 人工智能&#xff08;AI&#xff09;的概念最早可以追溯到20世纪40年代和50年代&#xff0c;当时的计算机科学家开始探索如何创建能模仿人类智能的机器。最初的AI研究集中在问题解决和符号逻辑上&#xff0c;但随着时间的推移&#xf…