18. 注入 Servlet、Filter、Listener


18.1 官方文档

18.2 基本介绍

  1. 考虑到实际开发业务非常复杂和兼容,Spring-Boot 支持将 Servlet、Filter、Listener 注入 Spring 容器,成为 Spring bean
  2. 也就是说 Spring-Boot 开放了和原生 WEB 组件(Servlet、Filter、Listener)的兼容

18.3 应用实例 1-使用注解方式注入

18.3.1 需求说明

  • 演示通过注解方式注入 Servlet、Filter、Listener

18.3.2 应用实例-实现

18.3.2.1 注入 Servlet
  1. 创建 com/xjs/springboot/servlet/Servlet_.java
package com.xjs.springboot.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Version: 1.0
 *
 * 1. 通过继承 HttpServlet 来开发原生的Servlet
 * 2. @WebServlet 标识将 Servlet_ 对象/Bean 注入到容器中
 * 3. urlPatterns = {"/servlet01", "/servlet02"} 对该 servlet配置了 url-pattern [映射路径]
 * 4. 特别提示:注入的原生的 Servlet_ 不会被 springboot 的拦截器拦截
 *    因为 我们这里的 Servlet_ 和 SpringMVC 的前端控制器-DispatcherServlet 属于同一级别
 *    当请求发来,就直接走到这里的 Servlet_ 而不会经过 Springmvc的那套机制
 *    ==> 回顾一下 前面学习的 过滤器和拦截器的区别
 *
 * 5. 对于开发的原生的Servlet,需要使用 @ServletComponentScan指定要扫描的原生Servlet包,
 *    才会注入到 spring 容器中
 */
@WebServlet(urlPatterns = {"/servlet01", "/servlet02"})
public class Servlet_ extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello, Servlet_");
    }
}

  1. 修改 com/xjs/springboot/Application.java ,加入@ServletComponentScan

在这里插入图片描述

package com.xjs.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Author: 谢家升
 * @Version: 1.0
 */
//要求扫描 com.xjs.springboot 包/子包 下的原生的方式 注入Servlet
@ServletComponentScan(basePackages = "com.xjs.springboot")
@SpringBootApplication
public class Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);

        System.out.println("hello");
    }
}

在这里插入图片描述

  1. 完成测试
  • 浏览器 http://localhost:8080/servlet01

在这里插入图片描述


  • 浏览器 http://localhost:8080/servlet02

在这里插入图片描述


18.3.2.2 注入 Filter
  1. 创建 com/xjs/springboot/servlet/Filter_.java
package com.xjs.springboot.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Version: 1.0
 *
 * 1. @WebFilter 表示 Filter_ 是一个过滤器,并注入到容器
 * 2. urlPatterns = {"/css/*", "/images/*"} 当请求 /css/目录资源 或者 /images/目录下资源 的时候会经过该过滤器
 * 3. 这里直接放行,放行后再经过拦截器 ,拦截器是否拦截需要根据拦截器的拦截规则
 */
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
@Slf4j
public class Filter_ implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Filter_ init() 方法被调用...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("Filter_ doFilter() 方法被调用...");
        //为了方便观察过滤器处理的资源,这里我们输出一下 uri
        String uri = ((HttpServletRequest) servletRequest).getRequestURI();
        log.info("过滤器处理的uri={}", uri);
        //这里直接放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        log.info("Filter_ destroy() 方法被调用...");
    }
}

  1. 创建 static/css/t.css,作为测试文件

在这里插入图片描述

  1. 完成测试,注意观察后台,浏览器 : http://localhost:8080/css/t.css

在这里插入图片描述


在这里插入图片描述

注意事项:

  • 过滤器配置的 urlPatterns 也会经过 Spring-Boot 拦截器(根据拦截器的规则)
  • 在 servlet 匹配全部是 /* ,在 Spring-Boot 是/**

在这里插入图片描述

在这里插入图片描述

18.3.2.3 注入 Listener
  1. 创建 com/xjs/springboot/servlet/Listener_.java
package com.xjs.springboot.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @Author: 谢家升
 * @Version: 1.0
 */
@Slf4j
@WebListener
public class Listener_ implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {

        //这里可以加入项目初始化的相关业务代码
        log.info("Listener_ contextInitialized() 项目初始化ok~");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //这里也可以加入相应代码
        log.info("Listener_ contextDestroyed() 项目销毁ok~");
    }
}

  1. 完成测试,启动项目,观察后台输出

在这里插入图片描述

18.4 应用实例 2-使用 RegistrationBean 方式注入

18.4.1 需求说明

  • 演示使用 RegistrationBean 注入 Servlet、Filter、Listener

18.4.2 应用实例-实现

  1. 创建 com/xjs/springboot/config/RegisterConfig_.java
package com.xjs.springboot.config;

import com.xjs.springboot.servlet.Filter_;
import com.xjs.springboot.servlet.Listener_;
import com.xjs.springboot.servlet.Servlet_;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

/**
 * @Author: 谢家升
 * @Version: 1.0
 * @Configuration 表示 RegisterConfig_ 是一个配置类
 * proxyBeanMethods = true : 默认是单例返回Bean [保证每个@Bean 方法被调用多少次返回的组件都是单实例的, 是代理方式]
 */
@Configuration(proxyBeanMethods = true)
public class RegisterConfig_ {

    //使用 RegistrationBean 注入 Servlet
    @Bean
    public ServletRegistrationBean servlet_() {
        //创建原生的 Servlet对象
        Servlet_ servlet_ = new Servlet_();

        //把 servlet_对象关联到 ServletRegistrationBean 对象中
        // "/servlet01","servlet02" 就是注入的 servlet的 uri-pattern
        return new ServletRegistrationBean(servlet_, "/servlet01", "/servlet02");
    }

    //使用 RegistrationBean 注入 Filter
    @Bean
    public FilterRegistrationBean filter_() {

        //创建原生的 Filter对象
        Filter_ filter_ = new Filter_();

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter_);
        //设置Filter的 url-pattern
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*", "/images/*"));

        return filterRegistrationBean;
    }


    //使用 RegistrationBean 注入 Listener
    @Bean
    public ServletListenerRegistrationBean listener_() {
        //创建原生的 Listener_对象
        Listener_ listener_ = new Listener_();

        return new ServletListenerRegistrationBean(listener_);
    }

}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 去掉相关的注解,再次完成测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

18.5 注意事项和细节说明

18.5.1 请求 Servlet 时,为什么不会到达拦截器

  1. 请求 Servlet 时,不会到达 DispatherServlet,因此也不会达到拦截器

  2. 原因分析

    ● 注入的 Servlet 会存在 Spring 容器中
    ● DispatherServlet 也存在 Spring 容器中


在这里插入图片描述


  1. 回顾一下 Tomcat 在对 Servlet url 匹配的原则,多个 servlet 都能处理到同一层路径,精确优先原则/最长前缀匹配原则
  2. 在看看 spring 容器的 debug 图

在这里插入图片描述

在这里插入图片描述

  1. 在 SpringBoot 中,去调用@Controller 目标方法 是按照 DispatherServlet 分发匹配的机制,回顾一下自己实现 SpringMVC 的底层机制的程序

18.5.2 源码分析

  1. DispatcherServletAutoConfiguration 完成对 DispatcherServlet 自动配置
  2. 下面执行流程分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 观察一下单例池中 dispatcherServletRegistration 和 DispatcherServlet 之间的关联关系

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要学就学灰太狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值