springmvc中常見的請(qǐng)求攔截器
- javax.servlet.Filter 的實(shí)現(xiàn)(默認(rèn)tomcat的實(shí)現(xiàn))
- org.springframework.web.servlet.HandlerInterceptor
那么Filter是在更外層,也就是在請(qǐng)求目標(biāo)方法之前先執(zhí)行Filter的攔截器,再執(zhí)行HandlerInterceptor
那么當(dāng)這兩個(gè)攔截器都不滿足某些場(chǎng)景,而這個(gè)場(chǎng)景又想要更靠近目標(biāo)方法執(zhí)行時(shí)機(jī)的時(shí)候,就想要找到一個(gè)更靠近請(qǐng)求目標(biāo)方法的擴(kuò)展點(diǎn),下述為尋找思路。
首先從HandlerInterceptor#preHandle入手
直接看這個(gè)方法由誰調(diào)用,如果沒有下載對(duì)應(yīng)源碼包,可以通過debug查看


// 將要調(diào)用目標(biāo)方法
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
跟蹤堆棧后進(jìn)入AbstractHandlerMethodAdapter#handle ,并且當(dāng)前實(shí)例是RequestMappingHandlerAdapter

繼續(xù)跟蹤

繼續(xù)跟蹤

然后我們可以看到執(zhí)行目標(biāo)方法 又委托給了ServletInvocableHandlerMethod

然后委托給invokeForRequest方法 再到doInvoke,走到這里我們已經(jīng)看到invokeForRequest中的邏輯,先通過
getMethodArgumentValues獲取到目標(biāo)方法的參數(shù),然后真正的執(zhí)行doInvoke方法帶上了參數(shù),可以預(yù)計(jì)到doInvoke方法已經(jīng)是非??拷鼒?zhí)行目標(biāo)方法了,所有準(zhǔn)備工作已經(jīng)完成,參數(shù)也構(gòu)造完了,并且所有方法public,protect也都沒有final,也就是這些方法都可以被重寫!
如果是重寫的話doInvoke是一個(gè)比較好的時(shí)機(jī),因?yàn)槟繕?biāo)方法的參數(shù)也構(gòu)造完畢。并且protect還不帶final真的太像是設(shè)計(jì)為被重寫的方法一樣。。。
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(method, getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
如上述重寫 doInvoke那么我們需要能夠繼承到當(dāng)前類InvocableHandlerMethod
突然又想到了上面debug過程中看到了構(gòu)建InvocableHandlerMethod這一行代碼如下
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 如果經(jīng)常閱讀源碼,上面所描述的 doInvoke是protect非final并且這里又構(gòu)建了對(duì)應(yīng)實(shí)例,這就很明顯了寫代碼的人就是這樣設(shè)計(jì)的,繼續(xù)進(jìn)入這個(gè)方法看個(gè)究竟
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 省略代碼 ...
那么我們進(jìn)入到 createInvocableHnadlerMethod方法查看
// 看到這里,繼續(xù)是protect,非final,方法名,以及這簡(jiǎn)單粗暴的return一個(gè)new 實(shí)例,堅(jiān)定了前面的所有想法!
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
到這里我們已經(jīng)非常確定以及肯定,doInvoke 以及ServletInvocableHandlerMethod 實(shí)例獲取方法createInvocableHandlerMethod就是被設(shè)計(jì)為讓我們可以擴(kuò)展的一系列操作
當(dāng)前實(shí)例為RequestMappingHandlerAdapter,那么我們就需要繼承它來重寫RequestMappingHandlerAdapter#createInvocableHandlerMethod就可以返回一個(gè)我們自己的ServletInvocableHandlerMethod來重寫ServletInvocableHandlerMethod#doInvoke
查找RequestMappingHandlerAdapter實(shí)例如果被創(chuàng)建
還是兩種方式,如果下載了對(duì)應(yīng)的包源碼直接ctrl點(diǎn)擊,或者debug到。
// 很明顯的一點(diǎn)是這個(gè)類是啟動(dòng)時(shí)就實(shí)例化了,InitializingBean這個(gè)接口是spring的bean注入過程中的鉤子之一(大部分帶有spring鉤子的類都是啟動(dòng)時(shí)注入spring容器,真的很少很少見動(dòng)態(tài)注入)
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean
我們直接debug構(gòu)造方法

直接來到 WebMvcConfigurationSupport#createRequestMappingHandlerAdapter
和上面的那些方法一樣,protect,非final,并且簡(jiǎn)單粗暴,看起來就是讓我們重寫的啊,那么我們就先切入這個(gè)點(diǎn)是否可以重寫

上面忘了說一點(diǎn),最主要的是要看每個(gè)類的注釋,尤其是類上面的注釋,多多少少可以理解作者的設(shè)計(jì)意圖,以及相關(guān)聯(lián)的其他類

上圖可以看到一個(gè)非常熟悉的東西,WebMvcConfigurer springmvc的各種擴(kuò)展點(diǎn)入口。既然WebMvcConfigurationSupport標(biāo)準(zhǔn)了可能有所關(guān)聯(lián),先看一眼。結(jié)果就是,沒啥可以重寫WebMvcConfigurationSupport的跡象。那么我們換個(gè)思路
如果你有下載了很多包的源碼可以看到 WebMvcConfigurationSupport#createRequestMappingHandlerAdapter已經(jīng)被重寫了

或者找到堆棧的上一個(gè)方法調(diào)用

@Override
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
// 這里我們眼前一亮,終于找到了真正的擴(kuò)展點(diǎn)!
if (this.mvcRegistrations != null) {
RequestMappingHandlerAdapter adapter = this.mvcRegistrations.getRequestMappingHandlerAdapter();
if (adapter != null) {
return adapter;
}
}
// 這里就是WebMvcConfigurationSupport#createRequestMappingHandlerAdapter 目前走的就是這個(gè)case
return super.createRequestMappingHandlerAdapter();
}
如下在EnableWebMvcConfiguration的構(gòu)造方法中可以看到 mvcRegistrations 我們只需要注入這個(gè)實(shí)現(xiàn)即可
private final WebMvcRegistrations mvcRegistrations;
private ResourceLoader resourceLoader;
public EnableWebMvcConfiguration(WebMvcProperties mvcProperties, WebProperties webProperties,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ListableBeanFactory beanFactory) {
this.resourceProperties = webProperties.getResources();
this.mvcProperties = mvcProperties;
this.webProperties = webProperties;
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
我們的重寫鏈路已經(jīng)浮現(xiàn)!WebMvcRegistrations(子類)#getRequestMappingHandlerAdapter返回RequestMappingHandlerAdapter(子類) --> RequestMappingHandlerAdapter(子類)#createInvocableHandlerMethod返回ServletInvocableHandlerMethod(子類) --> ServletInvocableHandlerMethod(子類)重寫 doInovke方法即可
下面看代碼
定義一個(gè)觀察者接口由客戶端程序可插入邏輯
import org.springframework.web.method.HandlerMethod;
/**
* @author ASDL3DL00676
*/
public interface ServletInvocableHandlerMethodInterceptor extends Ordered {
/**
* 通過參數(shù)來決定是否執(zhí)行
*
* @param args args
* @param handlerMethod 目標(biāo)方法
* @return boolean
*/
boolean supports(HandlerMethod handlerMethod, Object[] args);
/**
* 需要目標(biāo)方法考慮異常
* 前置方法
*
* @param args args
* @param handlerMethod 目標(biāo)方法
*/
default void beforeInvoke(HandlerMethod handlerMethod, Object[] args) {
}
/**
* 異常時(shí)
*
* @param args args
* @param throwable 拋出的異常
* @param handlerMethod 目標(biāo)方法
*/
default void afterThrows(HandlerMethod handlerMethod, Object[] args, Throwable throwable) {
}
/**
* 成功時(shí)需要目標(biāo)方法考慮異常
*
* @param args args
* @param result 結(jié)果值
* @param handlerMethod 目標(biāo)方法
*/
default void afterSuccess(HandlerMethod handlerMethod, Object[] args, Object result) {
}
/**
* 函數(shù)式包裹
*
* @param target 目標(biāo)方法
* @param handlerMethod controller 代理的方法
* @param args 參數(shù)
* @return 結(jié)果
*/
default Supplier<Object> wrapperDoInvoke(Supplier<Object> target, HandlerMethod handlerMethod,
Object[] args) {
return target;
}
/**
* 默認(rèn)排序
*
* @return int
*/
@Override
default int ordered() {
return 0;
}
}
初始化WebMvcRegistrations 注入spring
@Bean
// 同時(shí)允許依賴當(dāng)前jar的業(yè)務(wù)服務(wù)可以繼續(xù)重寫,如果想要保留當(dāng)前jar的邏輯需要繼承這里的WebMvcRegistrationsSimpleImpl
@ConditionalOnMissingBean
public WebMvcRegistrations customWebMvcRegistrations(
@Autowired(required = false) List<ServletInvocableHandlerMethodInterceptor> interceptors) {
if (CollectionUtils.isNotEmpty(interceptors)) {
interceptors.sort(Comparator.comparing(ServletInvocableHandlerMethodInterceptor::ordered));
}
return new WebMvcRegistrationsSimpleImpl(interceptors);
}
重寫WebMvcRegistrations 并帶上觀察者
import java.util.List;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* @author ASDL3DL00676
*/
@RequiredArgsConstructor
public class WebMvcRegistrationsSimpleImpl implements WebMvcRegistrations {
@Nullable
private final List<ServletInvocableHandlerMethodInterceptor> interceptors;
/**
* Return the custom {@link RequestMappingHandlerAdapter} that should be used and
* processed by the MVC configuration.
*
* @return the custom {@link RequestMappingHandlerAdapter} instance
*/
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new RequestMappingHandlerInterceptorImpl(interceptors);
}
}
重寫RequestMappingHandlerAdapter 并帶上觀察者
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
/**
* @author ASDL3DL00676
*/
@RequiredArgsConstructor
public class RequestMappingHandlerInterceptorImpl extends RequestMappingHandlerAdapter {
@Nullable
private final List<ServletInvocableHandlerMethodInterceptor> interceptors;
@Nonnull
@Override
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(@Nonnull HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethodWithPlugin(handlerMethod, interceptors);
}
}
重寫ServletInvocableHandlerMethod 并帶上觀察者,對(duì)doInvoke方法提供多種攔截,以及函數(shù)包裹邏輯
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.lang.Nullable;
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.mvc.method.annotation.ServletInvocableHandlerMethod;
/**
* @author ASDL3DL00676
*/
@EqualsAndHashCode(callSuper = true)
public class ServletInvocableHandlerMethodWithPlugin extends ServletInvocableHandlerMethod {
@javax.annotation.Nullable
private final List<ServletInvocableHandlerMethodInterceptor> interceptors;
private final HandlerMethod currentMethod;
public ServletInvocableHandlerMethodWithPlugin(
HandlerMethod handlerMethod,
@Nullable List<ServletInvocableHandlerMethodInterceptor> interceptors) {
super(handlerMethod);
this.interceptors = interceptors;
this.currentMethod = handlerMethod;
}
@Nullable
@Override
public Object invokeForRequest(
@Nonnull NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
@Nonnull Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
try {
Supplier<Object> target = () -> doInvoke(args);
if (CollectionUtils.isNotEmpty(interceptors)) {
for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
if (interceptor.supports(currentMethod, args)) {
interceptor.beforeInvoke(currentMethod, args);
target = interceptor.wrapperDoInvoke(target, currentMethod, args);
}
}
}
Object result = target.get();
if (CollectionUtils.isNotEmpty(interceptors)) {
for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
if (interceptor.supports(currentMethod, args)) {
interceptor.afterSuccess(currentMethod, args, result);
}
}
}
return result;
} catch (Throwable throwable) {
if (CollectionUtils.isNotEmpty(interceptors)) {
for (ServletInvocableHandlerMethodInterceptor interceptor : interceptors) {
if (interceptor.supports(currentMethod, args)) {
interceptor.afterThrows(currentMethod, args, throwable);
}
}
}
throw throwable;
}
}
}
到此我們找到了擴(kuò)展點(diǎn),并通過重寫對(duì)doInvoke添加觀察者,函數(shù)式包裹等,也可以通過其他的擴(kuò)展方法對(duì)
ServletInvocableHandlerMethod ,RequestMappingHandlerAdapter進(jìn)行擴(kuò)展