• SpringBoot拦截器和 Servlet3.0自定义Filter、Listener


    官方文档译文

    Spring Boot 包括对嵌入式TomcatJettyUndertow服务器的支持。大多数开发人员使用适当的“Starter”来获取完全配置的实例。默认情况下,嵌入式服务器在 port 8080上侦听 HTTP 请求。

    如果选择在CentOS上使用 Tomcat,请注意,默认情况下,临时目录用于存储已编译的 JSP,文件上载等。当 application 正在运行时,tmpwatch可能会删除此目录,从而导致失败。要避免此行为,您可能希望自定义tmpwatch configuration,以便不删除tomcat.*目录或配置server.tomcat.basedir,以便嵌入式 Tomcat 使用不同的位置。

    1 Servlets,Filters 和 listeners

    使用嵌入式 servlet 容器时,可以使用 Spring beans 或扫描 Servlet 组件,从 Servlet 规范中注册 servlets,过滤器和所有 listeners(如HttpSessionListener)。

    将 Servlets,Filters 和 Listeners 注册为 Spring Beans

    作为 Spring bean 的任何ServletFilter或 servlet *Listener实例都在嵌入式容器中注册。如果要在 configuration 期间从application.properties引用 value,这可能特别方便。

    默认情况下,如果 context 仅包含一个 Servlet,则它将映射到/。在多个 servlet beans 的情况下, bean name 用作路径前缀。将 map 过滤为/*

    如果 convention-based mapping 不够灵活,您可以使用ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean classes 进行完全控制。

    Spring Boot 附带了许多可能定义 Filter beans 的 auto-configurations。以下是过滤器及其各自 order 的一些示例(lower order value 表示更高的优先级):

    Servlet 过滤器订购
    OrderedCharacterEncodingFilter Ordered.HIGHEST_PRECEDENCE
    WebMvcMetricsFilter Ordered.HIGHEST_PRECEDENCE + 1
    ErrorPageFilter Ordered.HIGHEST_PRECEDENCE + 1
    HttpTraceFilter Ordered.LOWEST_PRECEDENCE - 10

    将 Filter beans 无序排列通常是安全的。

    如果需要特定的 order,则应避免配置在Ordered.HIGHEST_PRECEDENCE处读取请求正文的 Filter,因为它可能违反 application 的字符编码 configuration。如果 Servlet 过滤器包装请求,则应使用小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER的 order 进行配置。

    2 Servlet Context 初始化

    嵌入式 servlet 容器不直接执行 Servlet 3.0 javax.servlet.ServletContainerInitializer接口或 Spring 的org.springframework.web.WebApplicationInitializer接口。这是一个故意的设计决定,旨在降低第方库设计为 war 内部 war 的风险可能 break Spring Boot applications。

    如果需要在 Spring Boot application 中执行 servlet context 初始化,则应注册实现org.springframework.boot.web.servlet.ServletContextInitializer接口的 bean。单个onStartup方法提供对ServletContext的访问,如果需要,可以很容易地用作现有WebApplicationInitializer的适配器。

    扫描 Servlet,过滤器和 listeners

    使用嵌入式容器时,可以使用@ServletComponentScan启用使用@WebServlet@WebFilter@WebListener注释的 classes 的自动注册。

    @ServletComponentScan在独立容器中没有任何效果,而是使用容器的 built-in 发现机制。

    3 ServletWebServerApplicationContext

    在引擎盖下,Spring Boot 使用不同类型的ApplicationContext来嵌入 servlet 容器支持。 ServletWebServerApplicationContext是一种特殊类型的WebApplicationContext,它通过搜索单个ServletWebServerFactory bean 来引导自己。通常TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory已经是 auto-configured。

    您通常不需要知道这些 implementation classes。大多数 applications 都是 auto-configured,并且代表您创建了适当的ApplicationContextServletWebServerFactory

    4 自定义嵌入式 Servlet 容器

    可以使用 Spring Environment properties 配置 Common servlet 容器设置。通常,您将在application.properties文件中定义 properties。

    Common 服务器设置包括:

    • 网络设置:监听传入 HTTP 请求的 port(server.port),绑定到server.address的接口地址,依此类推。

    • Session 设置:session 是持久性的(server.servlet.session.persistence),session 超时(server.servlet.session.timeout),session 数据的位置(server.servlet.session.store-dir)和 session-cookie configuration(server.servlet.session.cookie.*)。

    • 错误 management:错误页面的位置(server.error.path)等。

    • SSL

    • HTTP 压缩

    Spring Boot 尝试尽可能多地暴露 common 设置,但这并不总是可行。对于这些情况,专用命名空间提供 server-specific 自定义(请参阅server.tomcatserver.undertow)。例如,可以使用嵌入的 servlet 容器的特定 features 配置访问日志

    有关完整列表,请参阅ServerProperties class。

    程序化定制

    如果需要以编程方式配置嵌入式 servlet 容器,可以注册实现WebServerFactoryCustomizer接口的 Spring bean。 WebServerFactoryCustomizer提供对ConfigurableServletWebServerFactory的访问,其中包括许多自定义 setter 方法。以下 example 以编程方式显示 port:

    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
    import org.springframework.stereotype.Component;
    ​
    @Component
    public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    ​
        @Override
        public void customize(ConfigurableServletWebServerFactory server) {
            server.setPort(9000);
        }
    ​
    }

    TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactoryConfigurableServletWebServerFactory的专用变体,它们分别为 Tomcat,Jetty 和 Undertow 提供了额外的自定义 setter 方法。

    直接自定义 ConfigurableServletWebServerFactory

    如果前面的自定义技术太有限,您可以自己注册TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactorybean。

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.setPort(9000);
        factory.setSessionTimeout(10, TimeUnit.MINUTES);
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
        return factory;
    }

    为许多 configuration 选项提供了 setter。如果您需要做一些更具异国情调的事情,还会提供一些受保护的方法“挂钩”。有关详细信息,请参阅source code 文档

    5 JSP 限制

    当 running 使用嵌入式 servlet 容器的 Spring Boot application(并打包为可执行存档)时,JSP 支持存在一些限制。

    • 使用 Jetty 和 Tomcat,如果使用 war 包装,它应该可以工作。可执行的 war 在使用java -jar启动时将起作用,并且还可以部署到任何标准容器。使用可执行文件 jar 时不支持 JSP。

    • Undertow 不支持 JSP。

    • 创建自定义error.jsp页面不会覆盖错误处理的默认视图。应该使用自定义错误页面代替。

    有一个JSP sample,所以你可以看到如何设置。

    代码

    1 Filter

    • 启动类

      @SpringBootApplication
      @ServletComponentScan
      public class FilterApplication {
      ​
          public static void main(String[] args) {
              SpringApplication.run(FilterApplication.class, args);
          }
      ​
      }
    • 过滤器

      **
       * @author WGR
       * @create 2019/11/14 -- 21:25
       */
      @WebFilter(urlPatterns = "/api/*", filterName = "loginFilter")
      public class LoginFilter implements Filter {
      ​
          /**
           * 容器加载的时候调用
           */
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              System.out.println("init loginFilter");
          }
      ​
      ​
          /**
           * 请求被拦截的时候进行调用
           */
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("doFilter loginFilter");
      ​
              HttpServletRequest req = (HttpServletRequest) servletRequest;
              HttpServletResponse resp = (HttpServletResponse) servletResponse;
              String username = req.getParameter("username");
      ​
              if ("topcheer".equals(username)) {
                  filterChain.doFilter(servletRequest,servletResponse);
              } else {
                  resp.sendRedirect("/index.html");
                  return;
              }
      ​
          }
      ​
          /**
           * 容器被销毁的时候被调用
           */
          @Override
          public void destroy() {
              System.out.println("destroy loginFilter");
          }
      }
    • web层

      /**
       * @author WGR
       * @create 2019/11/14 -- 21:32
       */
      ​
      @RestController
      public class LoginController {
      ​
          @GetMapping("/api/test_request")
          public Object testRequest(String username){
              return username;
          }
      }
    • html

      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      </head>
      <body>
      index static
      <h1>topcheer</h1>
      </body>
      </html>
    •  测试

         

     

     

     

    2 Servlet

    • servlet
    @WebServlet(name = "userServlet",urlPatterns = "/v1/api/test/customs")
    public class UserServlet extends HttpServlet{
    
         @Override
         public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             
             resp.getWriter().print("custom sevlet");
             resp.getWriter().flush();
             resp.getWriter().close();
         }
    
         
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            this.doGet(req, resp);
        }
    
         
        
    }
    • 测试方法
     @GetMapping("/v1/api/test/customs")
        public Object testServlet(){
            return "success";
        }
    • 测试结果

    3 Listener

    • listener
    @WebListener
    public class RequestListener implements ServletRequestListener {
    
        @Override
        public void requestDestroyed(ServletRequestEvent sre) {
            // TODO Auto-generated method stub
            System.out.println("======requestDestroyed========");
        }
    
        @Override
        public void requestInitialized(ServletRequestEvent sre) {
            System.out.println("======requestInitialized========");
    
        }
    
    
    }
    • 测试

     

     4 拦截器

    @Configuration
    public class CustomWebMvcConfigurer implements WebMvcConfigurer  {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
            registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api/*/**");
            registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/*/**");
            
            //.excludePathPatterns("/api2/xxx/**"); //拦截全部 /*/*/**
            
            WebMvcConfigurer.super.addInterceptors(registry);
        }
    
        
        
        
    
    
    }
    public class LoginIntercepter implements HandlerInterceptor{
    
        /**
         * 进入controller方法之前
         */
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
            System.out.println("LoginIntercepter------->preHandle");
    
    //        String token = request.getParameter("access_token");
    //        
    //        response.getWriter().print("fail");
            
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    
        /**
         * 调用完controller之后,视图渲染之前
         */
        @Override
        public void postHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            
            System.out.println("LoginIntercepter------->postHandle");
            
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 整个完成之后,通常用于资源清理
         */
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("LoginIntercepter------->afterCompletion");
            
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    
        
        
    }
    public class TwoIntercepter implements HandlerInterceptor{
    
        /**
         * 进入对应的controller方法之前
         */
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println("TwoIntercepter------>preHandle");
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    
        /**
         * controller处理之后,返回对应的视图之前
         */
        @Override
        public void postHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("TwoIntercepter------>postHandle");
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 整个请求结束后调用,视图渲染后,主要用于资源的清理
         */
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("TwoIntercepter------>afterCompletion");
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    
        
    }

    测试:

     总结:

    1、@Configuration
    继承WebMvcConfigurationAdapter(SpringBoot2.X之前旧版本)

    SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer

    2、自定义拦截器 HandlerInterceptor
    preHandle:调用Controller某个方法之前
    postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
    afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理

    3、按照注册顺序进行拦截,先注册,先被拦截

    拦截器不生效常见问题:
    1)是否有加@Configuration
    2)拦截路径是否有问题 ** 和 *
    3)拦截器最后路径一定要 “/**”, 如果是目录的话则是 /*/

    Filter
    是基于函数回调 doFilter(),而Interceptor则是基于AOP思想
    Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等

    依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。

    在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。

    Filter和Interceptor的执行顺序

    过滤前->拦截前->action执行->拦截后->过滤后

  • 相关阅读:
    [转]群控电梯调度算法
    [转] 电梯调度算法总结
    [转]grub2.0和之前版本修复解决方案
    [转]Ubuntu 10.04 编译安装最新版本Linux2.6.34内核
    [转]PS2 键盘工作方式
    [转]个人管理 - 目标管理之前,你会时间管理吗
    [转]ubuntu 下编译内核简单步骤
    [转]关闭Google安全搜索,实现无限制搜索
    [转]Vim 复制粘贴探秘
    [转]Linux文件搜索
  • 原文地址:https://www.cnblogs.com/dalianpai/p/11863283.html
Copyright © 2020-2023  润新知