谈谈spring中的拦截器interceptor

谈谈spring中的拦截器

       在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等等。今天就来详细的谈一下spring中的拦截器。spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。

一,HandlerInterceptor拦截器

HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {

	/**
	 * This implementation always returns <code>true</code>.
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
	    throws Exception {
		return true;
	}

	/**
	 * This implementation is empty.
	 */
	public void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception {
	}

	/**
	 * This implementation is empty.
	 */
	public void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}

}
       这三个方法都是干什么的,有什么作用,什么时候调用,不同的拦截器之间是怎样的调用顺序呢?这还得参考一下DispatcherServlet的doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		int interceptorIndex = -1;

		try {
			ModelAndView mv;
			boolean errorView = false;

			try {
				processedRequest = checkMultipart(request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// Apply preHandle methods of registered interceptors.
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// Do we need view name translation?
				if (mv != null && !mv.hasView()) {
					mv.setViewName(getDefaultViewName(request));
				}

				// Apply postHandle methods of registered interceptors.
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
			}
			catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered", ex);
				mv = ex.getModelAndView();
			}
			catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(processedRequest, response, handler, ex);
				errorView = (mv != null);
			}

			// Did the handler return a view to render?
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, response);
				if (errorView) {
					WebUtils.clearErrorRequestAttributes(request);
				}
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
							"': assuming HandlerAdapter completed request handling");
				}
			}

			// Trigger after-completion for successful outcome.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		}

		catch (Exception ex) {
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}
		catch (Error err) {
			ServletException ex = new NestedServletException("Handler processing failed", err);
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}

		finally {
			// Clean up any resources used by a multipart request.
			if (processedRequest != request) {
				cleanupMultipart(processedRequest);
			}
		}
	}

       代码有点长,但是它封装了springMVC处理请求的整个过程。首先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor拦截器;然后在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。关于拦截器的处理到此为止,接下来看看triggerAfterCompletion做了什么
private void triggerAfterCompletion(HandlerExecutionChain mappedHandler,
			int interceptorIndex,
			HttpServletRequest request,
			HttpServletResponse response,
			Exception ex) throws Exception {

		// Apply afterCompletion methods of registered interceptors.
		if (mappedHandler != null) {
			HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
			if (interceptors != null) {
				for (int i = interceptorIndex; i >= 0; i--) {
					HandlerInterceptor interceptor = interceptors[i];
					try {
						interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex);
					}
					catch (Throwable ex2) {
						logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
					}
				}
			}
		}
	}
       triggerAfterCompletion做的事情就是从当前的拦截器开始逆向调用每个拦截器的afterCompletion方法,并且捕获它的异常,也就是说每个拦截器的afterCompletion方法都会调用。
        根据以上的代码,分析一下不同拦截器及其方法的执行顺序。假设有5个拦截器编号分别为12345,若一切正常则方法的执行顺序是12345的preHandle,54321的postHandle,54321的afterCompletion。若编号3的拦截器的preHandle方法返回false或者抛出了异常,接下来会执行的是21的afterCompletion方法。这里要注意的地方是,我们在写一个拦截器的时候要谨慎的处理preHandle中的异常,因为这里一旦有异常抛出就不会再受到这个拦截器的控制。12345的preHandle的方法执行过之后,若handler出现了异常或者某个拦截器的postHandle方法出现了异常,则接下来都会执行54321的afterCompletion方法,因为只要12345的preHandle方法执行完,当前拦截器的拦截器就会记录成编号5的拦截器,而afterCompletion总是从当前的拦截器逆向的向前执行。
        另外,实现HandlerInterceptor拦截器还有一个方法,就是实现WebRequestInterceptor接口。其实它和刚才的两种方法也是殊途同归,最终还是被spring适配成HandlerInterceptor。有一点不同,它的preHandle方法最终只会返回true。

二,MethodInterceptor拦截器

         MethodInterceptor是AOP项目中的拦截器,它拦截的目标是方法,即使不是controller中的方法。实现MethodInterceptor拦截器大致也分为两种,一种是实现MethodInterceptor接口,另一种利用AspectJ的注解或配置。
下面是第一种方法的示例
public class MethodInvokeInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("before method invoke");
        Object object = methodInvocation.proceed();
        System.out.println("after method invoke");
        return object;
    }
}
下面是基于注解的AspectJ方式

@Component
@Aspect
public class AutoAspectJInterceptor {

    @Around("execution (* com.test.controller..*.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("AutoAspectJInterceptor begin around");
        Object object = point.proceed();
        System.out.println("AutoAspectJInterceptor end around");
        return object;
    }
   
}
下面是一个用于支持AspectJ方式拦截的普通的bean,当然你也可以在配置文件中声明这个bean
@Component
 public class AspectJInterceptor {
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("AspectJInterceptor around before");
        Object object = proceedingJoinPoint.proceed();
        System.out.println("AspectJInterceptor around after");
        return object;
    }
}
当然,这一切都离不开配置,具体看配置中的注释
<!-- 自定义拦截器 ,先过mvc:interceptors-->
	<bean id="methodInvokeInterceptor" class="com.test.interceptor.MethodInvokeInterceptor"/>
	<bean id="aspectInterceptor" class="com.test.interceptor.AspectJInterceptor"/>

	<aop:config>
		<!--切入点,controlller -->
		<aop:pointcut id="pointcut_test"	  expression="execution(* com.test.controller..*.*(..))" />
		<!--在该切入点使用自定义拦截器 ,按照先后顺序执行 -->
		<aop:advisor pointcut-ref="pointcut_test" advice-ref="methodInvokeInterceptor" />


		<aop:aspect ref="aspectInterceptor">
			<aop:around method="around" pointcut="execution(* com.test.controller..*.*(..))"/>
		</aop:aspect>
	</aop:config>
	<!-- 自动扫描使用了aspectj注解的类 -->
	<aop:aspectj-autoproxy/>



         通过上面的配置三个MethodInterceptor就能正常工作了。其实,这两种实现方式。。。。。最终。。。。。。没错,还是殊途同归。aspectj的拦截器会被解析成AOP中的advice,最终被适配成MethodInterceptor,详细的过程请参考springAOP的实现。

三,谈一谈区别

        上面的两种拦截器都能起到拦截的效果,但是他们拦截的目标不一样,实现的机制不同,所以有的时候适用不同的场景。HandlerInterceptoer拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。当你需要统计请求的响应时间时MethodInterceptor将不太容易做到,因为它可能跨越很多方法或者只涉及到已经定义好的方法中一部分代码。MethodInterceptor利用的是AOP的实现机制,在本文中只说明了使用方式,关于原理和机制方面介绍的比较少,因为要说清楚这些需要讲出AOP的相当一部分内容。在对一些普通的方法上的拦截HandlerInterceptoer就无能为力了,这时候只能利用AOP的MethodInterceptor。
        另外,还有一个跟拦截器类似的东西----Filter。 Filter是Servlet规范规定的,不属于spring框架,也是用于请求的拦截。但是它适合更粗粒度的拦截,在请求前后做一些编解码处理、日志记录等。而拦截器则可以提供更细粒度的,更加灵活的,针对某些请求、某些方法的组合的解决方案。
        另外的另外,用过人人网的ROSE框架的人都会非常喜欢它的拦截器功能。因为它实现了全注解的方式,只要在类的名字上加上拦截器的注解即表示这是一个拦截器。而使用这个拦截器的方法或者controller也只需在方法或controller的上面加上这个拦截器的注解。其实这是一个关注点的转变,spring的切面控制在配置文件中,配置文件关注哪些地方需要拦截。而在ROSE中,则是在需要拦截的地方关注我要被谁拦截。

以上纯属个人观点,欢迎交流!
<think>好的,我现在需要帮助用户了解Spring Boot中拦截器Interceptor)的工作原理及用法。首先,我应该回顾一下自己之前学过的相关知识,以及用户提供的引用内容。 根据用户给出的引用,特别是引用[2]和引用[4],拦截器的配置是通过实现WebMvcConfigurer接口,并重写addInterceptors方法。同时,拦截器的应用场景包括权限验证、参数预处理、耗时监控等。我需要将这些信息整合起来,以清晰的结构回答用户的问题。 首先,拦截器的工作原理是怎样的呢?拦截器Spring MVC中允许对请求进行预处理和后处理,类似于过滤器,但更深入框架内部。在Spring Boot中,由于自动配置,使用拦截器更加方便。需要提到HandlerInterceptor接口的三个方法:preHandle、postHandle、afterCompletion,这些是拦截器的核心方法。 接下来,如何具体使用拦截器?应该分步骤说明,比如创建拦截器类、注册拦截器到配置中。用户提供的代码示例来自引用[2],可以直接引用,但需要解释每个部分的作用,例如addPathPatterns和excludePathPatterns方法的使用。 然后,结合引用[4],需要提到拦截器的典型应用场景,如权限验证、日志记录等。这部分可以举例说明,比如登录检查拦截器的实现,如引用[2]中的LoginCheckInterceptor。 此外,需要注意拦截器与过滤器的区别,用户可能对这两者混淆。虽然用户没有直接提问,但作为补充内容可能会有帮助。根据知识库中的信息,拦截器属于Spring MVC,而过滤器是Servlet规范的一部分,执行顺序不同,拦截器可以访问处理器的上下文,而过滤器更底层。 最后,需要生成相关问题,引导用户进一步学习。例如,拦截器与过滤器的区别、多个拦截器的执行顺序、如何排除特定路径等。这些问题可以帮助用户更深入地理解拦截器的使用和配置。 在写作过程中,要确保遵循用户给出的格式要求,如使用中文回答,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及数学公式较少。同时,正确引用提供的引用内容,例如在提到应用场景时引用[4],在代码示例时引用[2]。 检查是否有遗漏的要点,比如是否需要详细说明每个方法(preHandle、postHandle、afterCompletion)的执行时机和返回值的作用。例如,preHandle返回false会中断请求,postHandle在处理器执行后但视图渲染前调用,afterCompletion在视图渲染完成后执行,用于资源清理等。 确保代码示例正确,解释清晰。用户提供的代码示例中使用了@Configuration和WebMvcConfigurer,需要说明这是配置类的标准做法,以及如何通过注册拦截器实例并设置路径模式。 可能还需要提到拦截器的执行顺序,如果有多个拦截器时的处理方式,比如按照注册顺序执行preHandle,逆序执行postHandle和afterCompletion。这部分内容如果用户没有提到,但作为进阶知识可以简要说明。 总结来说,回答的结构应该是:工作原理、实现步骤、应用场景、常见问题,最后是相关问题。确保每个部分都参考了用户提供的引用内容,并正确标注引用标记。</think>### Spring Boot拦截器(Interceptor)工作原理及用法详解 #### 一、拦截器核心原理 拦截器基于`HandlerInterceptor`接口实现,通过预处理、后处理机制拦截HTTP请求。在Spring Boot中需通过`WebMvcConfigurer`接口注册[^2],包含三个核心方法: 1. `preHandle()`: 请求到达Controller前执行,返回`true`放行,返回`false`中断流程 2. `postHandle()`: Controller执行后/视图渲染前触发 3. `afterCompletion()`: 整个请求完成后调用,常用于资源清理 $$流程图示例:请求 \rightarrow preHandle() \rightarrow Controller \rightarrow postHandle() \rightarrow 视图渲染 \rightarrow afterCompletion()$$ #### 二、实现步骤详解 1. **创建拦截器类** ```java public class DemoInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("--前置处理--"); return true; // 必须返回true才会继续执行 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { System.out.println("--后置处理--"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("--完成处理--"); } } ``` 2. **注册拦截器配置** ```java @Configuration public class WebConfig implements WebMvcConfigurer { @Bean public DemoInterceptor demoInterceptor() { return new DemoInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(demoInterceptor()) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns("/login"); // 排除登录页 } } ``` #### 三、典型应用场景[^4] | 场景类型 | 实现方式 | 业务价值 | |-----------------|-----------------------------------|------------------------------| | 接口权限验证 | preHandle中校验token/权限 | 防止未授权访问 | | 请求日志记录 | preHandle记录开始时间 | 统计接口性能 | | 数据格式转换 | postHandle统一包装返回值 | 标准化响应结构 | | 敏感操作审计 | afterCompletion记录操作日志 | 满足安全合规要求 | #### 四、高级配置技巧 1. **多拦截器执行顺序**:通过`Order`注解控制执行优先级 ```java registry.addInterceptor(A).order(1); registry.addInterceptor(B).order(2); // 数字越小优先级越高 ``` 2. **路径匹配模式**: - `/**`匹配所有路径(含子路径) - `/admin/*`匹配/admin下的单级路径 - `/api/**/v1`匹配多级路径中的v1接口 3. **排除静态资源**: ```java .excludePathPatterns("/css/**","/js/**") ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值