十三,Spring Boot 中注入 Servlet,Filter,Listener

打印 上一主题 下一主题

主题 574|帖子 574|积分 1722

十三,Spring Boot 中注入 Servlet,Filter,Listener

@
目录

1. 根本介绍


  • 考虑到现实开发业务非常复杂和兼容,Spring-Boot 支持将 Servlet,Filter ,Listener注入Spring容器,成为Spring bean
  • 也就是说明 Spring Boot 开放了和原生 WEB组件(Servlet,Filter,Listener)的兼容。
在Spring Boot 当中对应 Servlet,Filter (过滤器),Listener(监听器)的注入,有两种方式:

  • 第一种方式:使用注解方式注入 。
  • 第二种方式:使用 RegistrationBean方式注入 Servlet,Filter,Listener 的方式注入。
2. 第一种方式:使用注解方式注入:Servlet,Filter,Listener

2.1 使用注解方式注入:Servlet

使用(@WebServlet + @ServletComponentScan ) 这两个注解方式注入 Servlet
提示: urlPatterns = {"/servlet01","servlet02"},对Servlet设置了url-pat:请求路径的映射

  • 注入的原生的 Servlet_,不会被Spring boot的拦截器拦截
  • 对于开发的原生的Servlet,需要使用@ServletComponentScan指定要扫描的原生Servlet,才会注入到 Spring容器当中,留意:是在启动场景的位置添加该@ServletComponentScan注解。
  1. package com.rainbowsea.springboot.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.annotation.WebServlet;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. // 使用 extends 继承的方式(@WebServlet + @ServletComponentScan 注解),注入 servlet
  9. @WebServlet(urlPatterns = {"/servlet01","/servlet02"}) // 注意是: / 开头
  10. public class Servlet_ extends HttpServlet {
  11.     @Override
  12.     protected void doGet(HttpServletRequest request, HttpServletResponse response
  13.     ) throws ServletException,
  14.             IOException {
  15.         // 在前端显示打印显示一些信息。
  16.         response.getWriter().write("hello , Servlet_!");
  17.     }
  18. }
复制代码
留意需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。
  1. package com.rainbowsea.springboot;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.servlet.ServletComponentScan;
  5. import org.springframework.context.ConfigurableApplicationContext;
  6. @SpringBootApplication // 项目启动标志
  7. @ServletComponentScan(basePackages = {"com.rainbowsea.springboot"})
  8. public class Application {
  9.     public static void main(String[] args) {
  10.         ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);
  11.         //ioc.stop();  // 停止容器
  12.         System.out.println("hello");
  13.     }
  14. }
复制代码
运行测试:

2.2 使用注解方式注入:Filter

使用(@WebFilter+ @ServletComponentScan ) 这两个注解方式注入 Filter
留意注入的 Filter 过滤器要实现 implements javax.servlet.Filter 下的 Filter
  1. / 注意是:  javax.servlet.Filter 下的 Filter
  2. // 注入过滤器:(使用: @WebFilter(urlPatterns = {"/css/*","/images/*"}) + @ServletComponentScan(basePackages = {"com.rainbowsea.springboot"}))
  3. /*
  4. @WebFilter(urlPatterns = {"/css/*", "/images/*"})
  5. @WebFilter 表示 Filter_是一个过滤器,并注入容器
  6. urlPatterns = {"/css/*", "/images/*"} 当请求 /css/ 目录资源或者images
  7. 解读: 直接放行后,在经过拦截器,拦截器是否拦截要根据拦截器的拦截规则
  8. 特别说明在:之前下面这样配置的拦截器也是会拦截内容的。
  9. @Bean
  10.     public WebMvcConfigurer webMvcConfigurer() {
  11.         return new WebMvcConfigurer() {
  12.             @Override
  13.             public void addInterceptors(InterceptorRegistry registry) {
  14.                 System.out.println("addInterceptors~~~");
  15.                 // 注册拦截器
  16.                 registry.addInterceptor(new LoginInterceptor())
  17.                         .addPathPatterns("/**")
  18.                         .excludePathPatterns("/","/login","/images/**");
  19.             }
  20.         };
  21.     }
复制代码
留意:过滤器设置的urlPatterns 也会经过 Spring-Boot拦截器,所以为了
看到效果,请在拦截器设置放行 /css/**,
在 servlet 表示全部匹配是 "/*";而在 Spring boot 中表示全部匹配的是: "/**"
  1. package com.rainbowsea.springboot.servlet;
  2. import lombok.extern.slf4j.Slf4j;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.io.IOException;
  12. @Slf4j
  13. @WebFilter(urlPatterns = {"/static/css/*", "/images/*"})  // 注意:是/开头
  14. public class Filter_ implements Filter {
  15.     @Override
  16.     public void init(FilterConfig filterConfig) throws ServletException {
  17.         log.info("--Filter_ init0--");
  18.     }
  19.     @Override
  20.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  21.         log.info("Filter - doFitler");
  22.         // 为了方便观察过滤器处理的资源,我们输出一个url
  23.         HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
  24.         log.info("过滤器处理的 url={}",httpServletRequest.getRequestURI());
  25.         // 我们直接放行,实际开发中,根据自己的业务来决定如何处理
  26.         filterChain.doFilter(servletRequest, servletResponse);
  27.     }
  28.     @Override
  29.     public void destroy() {
  30.         log.info("Filter -destory");
  31.     }
  32. }
复制代码
同样留意:需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。

运行测试:

2.3 使用注解方式注入:Listener

使用(@WebListener+ @ServletComponentScan ) 这两个注解方式注入 Servlet
  1. package com.rainbowsea.springboot.servlet;
  2. import lombok.extern.slf4j.Slf4j;
  3. import javax.servlet.ServletContextEvent;
  4. import javax.servlet.ServletContextListener;
  5. import javax.servlet.annotation.WebListener;
  6. // 注入监听器(@WebListener + @ServletComponentScan(basePackages = {"com.rainbowsea.springboot"}))
  7. @Slf4j
  8. @WebListener
  9. public class Listener_ implements ServletContextListener {
  10.     @Override
  11.     public void contextInitialized(ServletContextEvent sce) {
  12.         // 这里可以加入项目初始化的相关业务代码
  13.         log.info("Listener_ contextInitialized 项目初始化OK~");
  14.     }
  15.     @Override
  16.     public void contextDestroyed(ServletContextEvent sce) {
  17.         // 这里可以加入相应代码...
  18.         log.info("Listener_ contextInitialized 项目销毁OK~");
  19.     }
  20. }
复制代码
同样留意:需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。

运行测试:


3. 第二种方式:使用 RegistrationBean 方式注入 Servlet,Filter,Listener

3.1 使用 RegistrationBean 方式注入 Servlet

  1. package com.rainbowsea.springboot.config;
  2. import com.rainbowsea.springboot.servlet.Filter_;
  3. import com.rainbowsea.springboot.servlet.Listener_;
  4. import com.rainbowsea.springboot.servlet.Servlet_;
  5. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  6. import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
  7. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import java.util.Arrays;
  11. // 使用  配置类的方式注入,servlet,和 Listener监听器,filter过滤器
  12. /*
  13. * @Configuration(proxyBeanMethods = true)
  14. * @Configuration 表示是一个配置类
  15. * proxyBeanMethods = true 默认是单例返回 bean(保证每个 @bean 方法被调用多少次,都是同一个)
  16. */
  17. @Configuration(proxyBeanMethods = true)
  18. public class RegisterConfig_ {
  19.     // 以使用RegistrationBean 方式
  20.     // 注入 Servlet
  21.     // 注意:要加上 Bean 对象
  22.     //@Bean(name = "Servlet_") // bean 没有指明name的话,默认是以方法名作为 name/id
  23.     @Bean
  24.     public ServletRegistrationBean servlet2() {
  25.         // 创建原生的 Servlet 对象(就是我们自己创建的 Servlet)
  26.         Servlet_ servlet_ = new Servlet_();
  27.         // 把 Servlet_ 对象 关联到 ServletRegistrationBean 对象
  28.         // "/servlet03" 就是注入Servlet的url-pattern
  29.         return new ServletRegistrationBean(servlet_, "/servlet03");
  30.     }
  31. }
复制代码
  1. package com.rainbowsea.springboot.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.annotation.WebServlet;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. public class Servlet_ extends HttpServlet {
  9.     @Override
  10.     protected void doGet(HttpServletRequest request, HttpServletResponse response
  11.     ) throws ServletException,
  12.             IOException {
  13.         // 在前端显示打印显示一些信息。
  14.         response.getWriter().write("hello , Servlet_!");
  15.     }
  16. }
复制代码
留意需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。
  1. package com.rainbowsea.springboot;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.servlet.ServletComponentScan;
  5. import org.springframework.context.ConfigurableApplicationContext;
  6. @SpringBootApplication // 项目启动标志
  7. @ServletComponentScan(basePackages = {"com.rainbowsea.springboot"})
  8. public class Application {
  9.     public static void main(String[] args) {
  10.         ConfigurableApplicationContext ioc = SpringApplication.run(Application.class, args);
  11.         //ioc.stop();  // 停止容器
  12.         System.out.println("hello");
  13.     }
  14. }
复制代码
运行测试:

3.2 使用 RegistrationBean 方式注入 Filter

  1. package com.rainbowsea.springboot.config;
  2. import com.rainbowsea.springboot.servlet.Filter_;
  3. import com.rainbowsea.springboot.servlet.Listener_;
  4. import com.rainbowsea.springboot.servlet.Servlet_;
  5. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  6. import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
  7. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import java.util.Arrays;
  11. // 使用  配置类的方式注入,servlet,和 Listener监听器,filter过滤器
  12. /*
  13. * @Configuration(proxyBeanMethods = true)
  14. * @Configuration 表示是一个配置类
  15. * proxyBeanMethods = true 默认是单例返回 bean(保证每个 @bean 方法被调用多少次,都是同一个)
  16. */
  17. @Configuration(proxyBeanMethods = true)
  18. public class RegisterConfig_ {
  19.     // 注入 Filter
  20.     // 注意:要加上 Bean 对象
  21.     @Bean(name = "Filter_")
  22.     public FilterRegistrationBean filter2() {
  23.         // 创建原生的 Filter_ 对象(就是我们自己创建的 Filter_)
  24.         Filter_ filter_ = new Filter_();
  25.         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter_);
  26.         // 设置 filter 的 url-pattern
  27.         // Arrays.asList("/css/*","images/*") 将字符串,转换为 集合
  28.         // 注意:不要漏 "/" 开头了。
  29.         filterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*", "/images/*"));
  30.         return filterRegistrationBean;
  31.     }
  32. }
复制代码
  1. package com.rainbowsea.springboot.servlet;
  2. import lombok.extern.slf4j.Slf4j;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.io.IOException;
  12. @Slf4j
  13. public class Filter_ implements Filter {
  14.     @Override
  15.     public void init(FilterConfig filterConfig) throws ServletException {
  16.         log.info("--Filter_ init0--");
  17.     }
  18.     @Override
  19.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  20.         log.info("Filter - doFitler");
  21.         // 为了方便观察过滤器处理的资源,我们输出一个url
  22.         HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
  23.         log.info("过滤器处理的 url={}",httpServletRequest.getRequestURI());
  24.         // 我们直接放行,实际开发中,根据自己的业务来决定如何处理
  25.         filterChain.doFilter(servletRequest, servletResponse);
  26.     }
  27.     @Override
  28.     public void destroy() {
  29.         log.info("Filter -destory");
  30.     }
  31. }
复制代码
同样留意:需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。

运行测试:

3.3 使用 RegistrationBean 方式注入 Listener

  1. package com.rainbowsea.springboot.config;
  2. import com.rainbowsea.springboot.servlet.Filter_;
  3. import com.rainbowsea.springboot.servlet.Listener_;
  4. import com.rainbowsea.springboot.servlet.Servlet_;
  5. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  6. import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
  7. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import java.util.Arrays;
  11. // 使用  配置类的方式注入,servlet,和 Listener监听器,filter过滤器
  12. /*
  13. * @Configuration(proxyBeanMethods = true)
  14. * @Configuration 表示是一个配置类
  15. * proxyBeanMethods = true 默认是单例返回 bean(保证每个 @bean 方法被调用多少次,都是同一个)
  16. */
  17. @Configuration(proxyBeanMethods = true)
  18. public class RegisterConfig_ {
  19.    
  20.     // 注入: Listener
  21.     //@Bean(name = "Listener_")
  22.     @Bean
  23.     public ServletListenerRegistrationBean Listener2() {
  24. // 创建原生的 Listener_ 对象(就是我们自己创建的 Listener_)
  25.         Listener_ listener_ = new Listener_();
  26.         return new ServletListenerRegistrationBean(listener_);
  27.     }
  28. }
复制代码
  1. package com.rainbowsea.springboot.servlet;
  2. import lombok.extern.slf4j.Slf4j;
  3. import javax.servlet.ServletContextEvent;
  4. import javax.servlet.ServletContextListener;
  5. import javax.servlet.annotation.WebListener;
  6. // 注入监听器(@WebListener + @ServletComponentScan(basePackages = {"com.rainbowsea.springboot"}))
  7. @Slf4j
  8. @WebListener
  9. public class Listener_ implements ServletContextListener {
  10.     @Override
  11.     public void contextInitialized(ServletContextEvent sce) {
  12.         // 这里可以加入项目初始化的相关业务代码
  13.         log.info("Listener_ contextInitialized 项目初始化OK~");
  14.     }
  15.     @Override
  16.     public void contextDestroyed(ServletContextEvent sce) {
  17.         // 这里可以加入相应代码...
  18.         log.info("Listener_ contextInitialized 项目销毁OK~");
  19.     }
  20. }
复制代码
同样留意:需要在对应项目标场景启动器的位置,使用@ServletComponentScan 注解,在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。

运行测试:


4. 留意事项和细节说明

请求 (自己所编写的)Servlet 时,为什么不会到达拦截器
请求 Servlet 时,不会到达 DispatherServlet,因此也不会到达拦截器
缘故起因分析:
注入的Servlet会存在Spring容器
DispatherServlet也存在Spring 容器
多个Servlet容器能处理到同一层拦截,精确优先原则/最长前缀匹配原则
所以当请求 /servlet01 时,就会直接匹配到注入的servlet
简朴的说:就是当你 servlet之间跳转通信的时候,是先找同一层的servlet,如果你同一层的
servlet有你所需要的映射的请求路径,那么优先跳转到servlet上,而不走 拦截器了,因为拦截器是在介于 servlet 和 Controller 控制器之间的。
大家可以回忆一下:我们讲过的 Tomcat 在对 Servlet url 匹配的原则,多个servlet都能处理到同一层路径,精确优先原则/最长前缀匹配原则
在Spring Boot 中,去调用@Controller 目标方法,是按照 DispatherServlet 分发匹配的机制,请同学们回顾一下,我们自己实现Spring MVC 的底层机制的程序。
5. 总结:


  • 第一种方式:使用注解方式注入Servlet,Filter,Listener:

    • 使用(@WebServlet + @ServletComponentScan ) 这两个注解方式注入 Servlet
    • 使用(@WebFilter+ @ServletComponentScan ) 这两个注解方式注入 Filter
    • 使用(@WebListener+ @ServletComponentScan ) 这两个注解方式注入 Servlet

  • 第二种方式:使用 RegistrationBean 方式注入 Servlet,Filter,Listener 。
  • 留意:无论是第一种方式照旧第二种方式,都必须在对应项目标场景启动器的位置上,使用上: @ServletComponentScan注解。在该注解的 basePackages 属性指明要让 Spring Boot扫描到的包的路径。让 Spring Boot可以找到你想让它注入的 ioc 容器当中的类。

6. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上汲取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表