接上一篇:SpringMVC异常统一处理并返回数据或视图View
我们知道SpringMVC可以通过拦截器处理preHandle,用来提前拦截权限、拦截登录,拦截很多业务逻辑。但是这个preHandle是没有Controller的入参的,因为org.springframework.web.servlet.DispatcherServlet#doDispatch
的实现中,调用preHandle后才执行Controller方法,这个时候才会有参数。
那么,如果需要这个参数,怎么办呢?
通过跟踪org.springframework.web.servlet.DispatcherServlet#doDispatch
,发现一直执行到org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
是,才能获取到Controller的入参,这个入参的获取会经过MessageConverter、DataBinder,因此一定不能从外面获取参数,否则其他配置都失效了,那么就只能在这个位置扩展,也就是自己实现InvocableHandlerMethod
。
通过查看代码发现是RequestMappingHandlerAdapter创建的InvocableHandlerMethod,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#createInvocableHandlerMethod
,而且是return new ServletInvocableHandlerMethod(handlerMethod);
,那么就只能自己实现RequestMappingHandlerAdapter
进行适配。
直接通过Aspect切面拦截Controller处理,理解上更简单一些。
WebMvcAutoConfiguration.EnableWebMvcConfiguration#EnableWebMvcConfiguration(ObjectProvider\ mvcPropertiesProvider, ObjectProvider\ mvcRegistrationsProvider, ListableBeanFactory beanFactory)
可以提供一个ObjectProvider,第二个参数是WebMvcRegistrations
,可以提供一个RequestMappingHandlerAdapter
,那么就通过这里扩展。WebMvcRegistrations
的实现类,并提供自定义的RequestMappingHandlerAdapter
RequestMappingHandlerAdapter#createInvocableHandlerMethod
方法,返回自定义的ServletInvocableHandlerMethod
ExtendServletInvocableHandlerMethod
,扩展ServletInvocableHandlerMethod
,覆盖invokeForRequest
方法,在其中增加拦截器操作HandlerInterceptor
,将这个拦截器也注册到SpringMVC中,既可以使用拦截器的各种功能,又可以执行带参数的拦截方法。注:在HandlerInterceptor的preHandle中写requestAttribute,在新拦截器的preHandle里面判断并执行,即可实现在这个位置进行拦截(这里逻辑比较复杂,具体看下面的实现)package com.xxxxx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 唯一的配置{@link RequestMappingHandlerAdapter},增加一个扩展拦截器,获取到参数之后,执行controller之前
*
* @author obiteaaron
* @see WebMvcAutoConfiguration.EnableWebMvcConfiguration#EnableWebMvcConfiguration(org.springframework.beans.factory.ObjectProvider, org.springframework.beans.factory.ObjectProvider, org.springframework.beans.factory.ListableBeanFactory)
* @since 2020/1/7
*/
@Component
public class WebMvcRegistrationsInterceptorExtend implements WebMvcRegistrations {
@Autowired
private ExtendHandlerInterceptor extendHandlerInterceptor;
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter() {
@Override
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
@Override
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ExtendServletInvocableHandlerMethod(handlerMethod, extendHandlerInterceptor);
}
};
}
/**
* 自定义方法处理器,可以在执行时拦截到参数
*/
public static class ExtendServletInvocableHandlerMethod extends ServletInvocableHandlerMethod {
private ExtendHandlerInterceptor extendHandlerInterceptor;
public ExtendServletInvocableHandlerMethod(Object handler, Method method) {
super(handler, method);
}
public ExtendServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
public ExtendServletInvocableHandlerMethod(HandlerMethod handlerMethod, ExtendHandlerInterceptor extendHandlerInterceptor) {
super(handlerMethod);
this.extendHandlerInterceptor = extendHandlerInterceptor;
}
/**
* copy from super
*/
@Override
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 由于springMVC拦截器并没有参数,这里提供带参数的拦截器(当然,用AOP代理Controller也是一种实现方式)
boolean interceptor = doInterceptor(request.getNativeRequest(HttpServletRequest.class), request.getNativeRequest(HttpServletResponse.class), args);
if (!interceptor) {
return null;
}
return doInvoke(args);
}
private boolean doInterceptor(HttpServletRequest request, HttpServletResponse response, Object[] args) throws Exception {
return extendHandlerInterceptor.handle(request, response, this, args);
}
}
/**
* 依然使用拦截器做,这样可以注册到Spring中,可以使用拦截器的包含、排除功能。
* 这个拦截器,既要配置到ExtendServletInvocableHandlerMethod中,也要配置到SpringMVC的拦截器中。
*/
public interface ExtendHandlerInterceptor extends HandlerInterceptor {
@Override
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
setNeedInterceptor(request);
return true;
}
default boolean handle(HttpServletRequest request, HttpServletResponse response, ExtendServletInvocableHandlerMethod extendServletInvocableHandlerMethod, Object[] args) throws Exception {
try {
if (isNeedInterceptor(request)) {
return preHandle(request, response, extendServletInvocableHandlerMethod, args);
}
return true;
} finally {
clearInterceptor(request);
}
}
/**
* 这里如果错误,直接抛出异常
*
* @see #isNeedInterceptor(HttpServletRequest)
*/
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler, Object[] args) throws Exception;
/**
* 拦截器处,仅包含写入拦截信息
*/
default void setNeedInterceptor(HttpServletRequest request) {
request.setAttribute("NEED_INTERCEPTOR", true);
}
/**
* 如果有拦截信息,才进行拦截
*/
default boolean isNeedInterceptor(HttpServletRequest request) {
return Boolean.TRUE.equals(request.getAttribute("NEED_INTERCEPTOR"));
}
/**
* 使用完成后删除
*/
default void clearInterceptor(HttpServletRequest request) {
request.removeAttribute("NEED_INTERCEPTOR");
}
}
}
package com.xxxxx;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author obiteaaron
* @since 2020/1/7
*/
public class ExtendInterceptor extends BaseInterceptor implements WebMvcRegistrationsInterceptorExtend.ExtendHandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return WebMvcRegistrationsInterceptorExtend.ExtendHandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler, Object[] args) throws Exception {
boolean checkResult = ((ExtendInterceptorPreHandler) interceptorPreHandler).check(request, response, handler, args);
if (!checkResult) {
postInterceptor(request, response, handler);
return false;
} else {
return true;
}
}
@Override
public void setInterceptorPreHandler(InterceptorPreHandler interceptorPreHandler) {
throw new UnsupportedOperationException("");
}
public void setExtendInterceptorPreHandler(ExtendInterceptorPreHandler extendInterceptorPreHandler) {
this.interceptorPreHandler = extendInterceptorPreHandler;
}
public interface ExtendInterceptorPreHandler extends InterceptorPreHandler {
@Override
default boolean check(HttpServletRequest request, HttpServletResponse response, Object handler) {
throw new UnsupportedOperationException("");
}
/**
* @see WebMvcRegistrationsInterceptorExtend.ExtendHandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Object[])
*/
boolean check(HttpServletRequest request, HttpServletResponse response, Object handler, Object[] args) throws Exception;
/**
* 拦截后返回的视图名称
*
* @see ModelAndView
* @see ViewNameMethodReturnValueHandler
*/
String getViewName();
/**
* 拦截后返回的对象
*
* @see ResponseBody
* @see RequestResponseBodyMethodProcessor
*/
Object getResponseBody();
}
}