• SpringWeb 拦截器


    前言

    spring拦截器能帮我们实现验证是否登陆、验签校验请求是否合法、预先设置数据等功能,那么该如何设置拦截器以及它的原理如何呢,下面将进行简单的介绍

    1.设置

    HandlerInterceptor接口
    public interface HandlerInterceptor {
    
    	/**
    	 * Intercept the execution of a handler. Called after HandlerMapping determined
    	 * an appropriate handler object, but before HandlerAdapter invokes the handler.
    	 * 

    DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can decide to abort the execution chain, * typically sending a HTTP error or writing a custom response. *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. *

    The default implementation returns {@code true}. * @param request current HTTP request * @param response current HTTP response * @param handler chosen handler to execute, for type and/or instance evaluation * @return {@code true} if the execution chain should proceed with the * next interceptor or the handler itself. Else, DispatcherServlet assumes * that this interceptor has already dealt with the response itself. * @throws Exception in case of errors */ default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } /** * Intercept the execution of a handler. Called after HandlerAdapter actually * invoked the handler, but before the DispatcherServlet renders the view. * Can expose additional model objects to the view via the given ModelAndView. *

    DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can post-process an execution, * getting applied in inverse order of the execution chain. *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. *

    The default implementation is empty. * @param request current HTTP request * @param response current HTTP response * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param modelAndView the {@code ModelAndView} that the handler returned * (can also be {@code null}) * @throws Exception in case of errors */ default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } /** * Callback after completion of request processing, that is, after rendering * the view. Will be called on any outcome of handler execution, thus allows * for proper resource cleanup. *

    Note: Will only be called if this interceptor's {@code preHandle} * method has successfully completed and returned {@code true}! *

    As with the {@code postHandle} method, the method will be invoked on each * interceptor in the chain in reverse order, so the first interceptor will be * the last to be invoked. *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. *

    The default implementation is empty. * @param request current HTTP request * @param response current HTTP response * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param ex exception thrown on handler execution, if any * @throws Exception in case of errors */ default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }

    自定义拦截器需要实现HandlerInteceptor接口,该接口有三个方法:

    preHandle:主要在映射适配器执行handler之前调用,若返回为true则继续往下执行handler,若返回为false则直接返回不继续处理请求

    postHandle:主要在适配器执行handler之后调用 

    afterCompletion:在postHandle后调用可清理一些数据,若preHandle返回false那么会调用完此方法后再返回

    @Component
    public class CustomInterceptor implements HandlerInterceptor {
    
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
          throws Exception {
        System.out.println("-------------拦截请求:" + request.getRequestURI() + "-------------");
        // 可以根据request设置请求头、或从请求头提取信息等等...
        return true;
      }
    
      @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
          @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ....");
      }
    
      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
          @Nullable Exception ex) throws Exception {
        System.out.println("afterCompletion ....");
      }
    }
    折叠

    接着创建配置类,实现WebMvcConfigurer接口,重写addInterceptors方法将自定义拦截器添加,并且加上@EnableWebMvc注解 (springboot项目会自动配置)

    @Configuration
    @EnableWebMvc
    public class MyMvcConfigurer implements WebMvcConfigurer {
    
      @Resource
      private CustomInterceptor customInterceptor;
    
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customInterceptor)
            .addPathPatterns("/**");
      }
    }

    配置完之后启动项目访问某个url路径,从控制台可以看到拦截器确实生效了

     

    2.原理

    首先是@EnableWebMvc注解,spring会解析并导入DelegatingWebMvcConfiguration这个bean,继承关系如下,主要逻辑都写在父类WebMvcConfigurationSupport中

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }

    WebMvcConfigurationSupport中会创建一个映射处理器RequestMappingHandlerMapping

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    	mapping.setOrder(0);
    	// 设置拦截器到mapping
    	mapping.setInterceptors(getInterceptors());
    	// 设置内容协商管理器
    	mapping.setContentNegotiationManager(mvcContentNegotiationManager());
    	// 跨域配置
    	mapping.setCorsConfigurations(getCorsConfigurations());
    
    	// 路径匹配设置
    	PathMatchConfigurer configurer = getPathMatchConfigurer();
    
    	Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
    	if (useSuffixPatternMatch != null) {
    		mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
    	}
    	Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
    	if (useRegisteredSuffixPatternMatch != null) {
    		mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
    	}
    	Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
    	if (useTrailingSlashMatch != null) {
    		mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    	}
    
    	UrlPathHelper pathHelper = configurer.getUrlPathHelper();
    	if (pathHelper != null) {
    		mapping.setUrlPathHelper(pathHelper);
    	}
    	PathMatcher pathMatcher = configurer.getPathMatcher();
    	if (pathMatcher != null) {
    		mapping.setPathMatcher(pathMatcher);
    	}
    
    	return mapping;
    }
    
    
    #获取拦截器
    protected final Object[] getInterceptors() {
    	if (this.interceptors == null) {
    		InterceptorRegistry registry = new InterceptorRegistry();
    		// 调用DelegatingWebMvcConfiguration.addInterceptors 添加自定义的拦截器
    		addInterceptors(registry);
    		registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
    		registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
    		// 获取拦截器并根据order排序,若有匹配路径则封装成MappedInterceptor
    		this.interceptors = registry.getInterceptors();
    	}
    	return this.interceptors.toArray();
    }
    折叠

    注意这一行代码mapping.setInterceptors(getInterceptors());  getInterceptors方法会调用子类DelegatingWebMvcConfiguration的addInterceptors方法,接着会调用委托类即我们自定义配置类MyMvcConfigurer类的addInterceptors方法,将自定义的拦截器添加到拦截器注册类中,而后通过拦截器注册类获取到拦截器列表,最后将拦截器添加到映射处理器handlerMapping中,供后续使用。

    最后看下请求处理的DispatcherServlet#doDispatch方法 (为了看的更清楚一点删掉了一些代码)

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	HttpServletRequest processedRequest = request;
    	// 处理程序执行链
    	HandlerExecutionChain mappedHandler = null;
    
    	try {
    		ModelAndView mv = null;
    		Exception dispatchException = null;
    
    		try {
    			// Determine handler for the current request.
    			// 遍历handlerMapping获取能处理request的处理器,mappedHandler里封装着之前我们定义的拦截器供后续调用
    			mappedHandler = getHandler(processedRequest);
    			if (mappedHandler == null) {
    				noHandlerFound(processedRequest, response);
    				return;
    			}
    
    			// Determine handler adapter for the current request.
    			// 确定处理当前请求的处理适配器 RequestMappingHandlerAdapter
    			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    			// 执行handler之前应用拦截器执行拦截器的后置方法 返回为false表示请求不合理直接返回了
    			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    				return;
    			}
    
    			// Actually invoke the handler.
    			// 真正执行这个HandlerMethod
    			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                
    			applyDefaultViewName(processedRequest, mv);
    			// 执行拦截器的后置方法
    			mappedHandler.applyPostHandle(processedRequest, response, mv);
    		}
    		catch (Exception ex) {
    			dispatchException = ex;
    		}
    		catch (Throwable err) {
    			// As of 4.3, we're processing Errors thrown from handler methods as well,
    			// making them available for @ExceptionHandler methods and other scenarios.
    			dispatchException = new NestedServletException("Handler dispatch failed", err);
    		}
    		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    	}
    	catch (Exception ex) {
    		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    	}
    	catch (Throwable err) {
    		triggerAfterCompletion(processedRequest, response, mappedHandler,
    				new NestedServletException("Handler processing failed", err));
    	}
    	finally {
    
    	}
    }
    
    
    #mappedHandler.applyPreHandle
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	HandlerInterceptor[] interceptors = getInterceptors();
    	if (!ObjectUtils.isEmpty(interceptors)) {
    		for (int i = 0; i < interceptors.length; i++) {
    			HandlerInterceptor interceptor = interceptors[i];
    			// 前置处理为false时
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				// 触发拦截器的afterCompletion方法
    				triggerAfterCompletion(request, response, null);
    				return false;
    			}
    			this.interceptorIndex = i;
    		}
    	}
    	return true;
    }
    折叠

    可以看到再真正执行handler之前会调用mappedHandler.applyPreHandle 方法,遍历拦截器执行preHandle方法,若返回false则根据先前执行过的拦截器顺序倒序执行afterCompletion方法,都通过的话后续执行handler获取请求结果,再接着执行拦截器的postHandle方法最后执行afterCompletion方法。

  • 相关阅读:
    Perl爬虫程序的框架
    python 练习9.14
    贪心算法例子
    浅谈C++|STL之vector篇
    使用tomcat来部署jenkins
    用户社交信息交互卡片
    C++入门【下】—— 预处理与内联函数关系
    用ideaIU写 远程操控我电脑写 仅限idealu不要python 数据我不会上传 但是截图太模糊了 所以直接发文字了 13726116957加我wx 切记要远程控制我电脑写
    【Mac】记录一次Mac配置Maven无效的经历:zsh: operation not permitted: mvn
    凯美瑞 vs 太空船:Web3 游戏生长的两条路径
  • 原文地址:https://www.cnblogs.com/monianxd/p/16502791.html