SpringBoot-注入 Servlet、Filter、Listener

1.官方文档

1.文 档

2.基本介绍

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

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

1.需求 : 演示通过注解方式注入 Servlet、Filter、Listener

主启动类添加@ServletComponentScan,添加包扫描这样注入的servlet、filter、listener才能注入ioc容器

springboot 项目启动时去扫描指定compon springboot指定扫描包_spring

需要注意的是注入ioc容器中的servlet,key为类的全路径,而注入的类型是ServletRegistrationBean.class

springboot 项目启动时去扫描指定compon springboot指定扫描包_java_02

去掉@ServletComponentScan包扫描,无法注入ioc容器

springboot 项目启动时去扫描指定compon springboot指定扫描包_java_03

2.应用实例-实现

1.注入servlet

1.创建src\main\java\com\llp\springboot\servlet\Servlet_.java

/**
 * 1. 通过继承 HttpServlet 来开发原生的Servlet
 * 2. @WebServlet 标识将 Servlet_ 对象/bean注入到容器
 * 3. (urlPatterns = {"/servlet01","/servlet02"} ,对servlet配置了url-pattern
 * 4. 提示: 注入的原生的Servlet_ 不会被spring-boot的拦截器拦截
 * 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 {
        doPost(req,resp);
    }

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

2.修改主启动添加包扫描

@SpringBootApplication
//要求扫描com.llp.springboot 包/子包下的原生方式注入的Servlet
@ServletComponentScan(basePackages = "com.llp.springboot")
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);
        ServletRegistrationBean servletRegistrationBean = ioc.getBean("com.llp.springboot.servlet.Servlet_", ServletRegistrationBean.class);
        Servlet servlet = servletRegistrationBean.getServlet();
        System.out.println(servlet);
        System.out.println(ioc);

    }
}

3.测试

springboot 项目启动时去扫描指定compon springboot指定扫描包_ide_04

2.注入filter

1.创建src\main\java\com\hspedu\springboot\servlet\Filter_.java

@Slf4j
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
public class Filter_ implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("--Filter_ init--");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("--Filter_ doFilter--");
        //为了方便观察过滤器处理的资源,我们输出一个uri
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info("过滤器处理的uri={}", httpServletRequest.getRequestURI());
        //我们直接放行-实际开发中,根据自己的业务来决定如何处理
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        log.info("--Filter_ destroy--");
    }
}

2.创建src\main\resources\static\css\t.css

hello

3.测试http://localhost:8080/css/t.css

springboot 项目启动时去扫描指定compon springboot指定扫描包_spring boot_05

3.注入Listener

**ServletContextListener:**监听 ServletContext 创建或销毁(当我们Web 应用启动时,就会创建 ServletContext), 即生命周期监听,

应用场景(1)加载初始化的配置文件;比如 spring 的配置文件 (2)任务调度(配合定时器 Timer/TimerTask)

@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 项目销毁");
    }
}

测试

springboot 项目启动时去扫描指定compon springboot指定扫描包_java_06

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

1.需求 : 演示使用 RegistrationBean 注入 Servlet、Filter、Listener

2.应用实例-实现

通过配置类的方式注入,不需要添加@WebListener、@WebFilter、@WebServlet同时启动类的包扫描也是不需要的

1.创建配置类

/**
 * proxyBeanMethods = true : 默认是单例返回bean[保证每个@Bean 方法被调用多少次返回的组件都是 单实例的, 是代理方式]
 */
@Configuration(proxyBeanMethods = true)
public class RegisterConfig {

    //注入servlet
    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        return new ServletRegistrationBean(new Servlet_(),"/servlet01","/servlet02");
    }

    //注入filter
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new Filter_());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*", "/images/*"));
        return filterRegistrationBean;
    }

    //注入listener
    @Bean
    public ServletListenerRegistrationBean servletListenerRegistrationBean(){
        ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
        servletListenerRegistrationBean.setListener(new Listener_());
        return servletListenerRegistrationBean;
    }

}

2.创建servlet、listener、filter

servlet

/**
 * 1. 通过继承 HttpServlet 来开发原生的Servlet
 * 2. @WebServlet 标识将 Servlet_ 对象/bean注入到容器
 * 3. (urlPatterns = {"/servlet01","/servlet02"} ,对servlet配置了url-pattern
 * 4. 提示: 注入的原生的Servlet_ 不会被spring-boot的拦截器拦截
 * 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 {
        doPost(req,resp);
    }

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

filter

/**
 * 开发Filter 并注入
 *
 * 1. @WebFilter 表示Filter_是一个过滤器,并注入容器
 * 2. urlPatterns = {"/css/*", "/images/*"} 当请求  /css/目录资源或者 /images/目录下资源的时候,会经过该过滤器
 * 3. 直接放行后,在经过拦截器, 拦截器是否拦截要根据拦截器的拦截规则
 */
@Slf4j
//@WebFilter(urlPatterns = {"/css/*", "/images/*"})
public class Filter_ implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("--Filter_ init--");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("--Filter_ doFilter--");
        //为了方便观察过滤器处理的资源,我们输出一个uri
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info("过滤器处理的uri={}", httpServletRequest.getRequestURI());
        //我们直接放行-实际开发中,根据自己的业务来决定如何处理
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        log.info("--Filter_ destroy--");
    }
}

listener

@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 项目销毁");
    }
}

5.注意事项和细节说明

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

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

√ 注入的 Servlet 会存在 Spring 容器

√ DispatherServlet 也存在 Spring 容器

springboot 项目启动时去扫描指定compon springboot指定扫描包_spring boot_07

  1. Tomcat 在对 Servlet url 匹配的原则, 多个 servlet 都能处理到 同一层路径, 精确优先原则/最长前缀匹配原则.
  2. 精确路径 > 目录路径 > 扩展名路径 > /* > /
  3. 在看看 spring 容器的debug图

springboot 项目启动时去扫描指定compon springboot指定扫描包_servlet_08

6.在 SpringBoot 中, 去调用@Controller 目标方法 是按照 DispatherServlet 分发匹配的机制

2.源码分析

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

springboot 项目启动时去扫描指定compon springboot指定扫描包_servlet_09

springboot 项目启动时去扫描指定compon springboot指定扫描包_spring boot_10

6.拦截器和过滤器的区别

springboot 项目启动时去扫描指定compon springboot指定扫描包_spring_11

1.过滤器是拦截tomcat对servlet的请求

2.拦截器是拦截servlet到controller的请求分发

3.过滤器不会拦截请求转发,拦截器会拦截请求转发。因为请求转发由servlet在服务端发起,过滤器不会进行拦截

4.controller层的请求由dispatcherServlet进行分发,而dispatcherServlet是一个servlet