1 过程
1、创建自定义拦截器类MyInterceptor
2、注册拦截器
@Configuration
public class WebConf extends WebMvcConfigurerAdapter {
@Autowired
MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}
} |
3、调用某个接口,该接口是经过zuul转发的,发现请求并未经过上面创建的自定义拦截器
4、通过论坛查找资料,发现需要通过扩展spring的扩展点,把自定义拦截器添加进去
@Configuration
public class ZuulHandlerBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
@Autowired
private MyInterceptor myInterceptor;
@Override
public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
if (bean instanceof ZuulHandlerMapping) {
ZuulHandlerMapping zuulHandlerMapping = (ZuulHandlerMapping) bean;
zuulHandlerMapping.setInterceptors(myInterceptor);
}
return super.postProcessAfterInstantiation(bean, beanName);
}
} |
2 原因分析
原以为zuul Filter是实现了tomcat的Filter接口,后来发现不是的,zuul Filter是zuul自己实现的一套Filter规范,zuul有自己的ZuulController,可以认为zuul就是一个普通的controller,请求打到zuul和请求打到其他controller一样,都是需要经过tomcatFilter和spring mvc的过程。那么我们经过上面所说的第2步,将自定义过滤器注册成功后,为什么打到zuul的请求没有被拦截器拦截到呢?接下来我们分析一下原因。
mvc请求转发过程分析
工程中使用了spring mvc,因为zuul就是一个controller,所以打到zuul的请求也是经过mvc的DispatcherServlet进行的转发(mvc的请求流转整体过程不了解的可自行搜索其他资料)。下面我们对DispatcherServlet的doDispatch方法进行分析:
DispatcherServlet源码分析
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//根据request找handler
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//根据handler找handlerAdapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
//执行相应的Interceptor的prehandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//HandlerAdapter使用handler处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
。。。
}
} |
关键步骤已经在代码中注释。
关键是怎么找到HandlerExecutionChain mappedHandler的
那么,我们可以看下this.getHandler(processedRequest);方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Iterator var2 = this.handlerMappings.iterator();
HandlerExecutionChain handler;
do {
if (!var2.hasNext()) {
return null;
}
HandlerMapping hm = (HandlerMapping)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
handler = hm.getHandler(request);
} while(handler == null);
return handler;
} |
这里是通过遍历handlerMappings查找处理器,这里的关键是查找handler是按照顺序遍历所有的handlerMapping,找到一个后立即停止并返回,不会再遍历后续的handlerMapping,所以这里handlerMapping的顺序很重要。
我们再分析一下 handler = hm.getHandler(request); 这行代码是如何实现查找handler的。这里就进入了具体的HandlerMapping的逻辑
下图是HandlerMapping接口的实现类继承图,供参考
HandlerMapping的作用就是根据request查找Handler和Interceptors
下图是ZuulHandlerMapping的继承图
ZuulHandlerMapping的getHandler(request)方法其父类AbstractHandlerMapping.getHandler方法,源码如下
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
if (handler == null) {
handler = this.getDefaultHandler();
}
if (handler == null) {
return null;
} else {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
} |
HandlerMapping的作用就是根据request查找Handler和Interceptors,获取Handler的过程是通过模板方法getHandlerInternal交给了子类。
getHandler方法的实现共分两部分,gethandlerExecutionChain之前是找Handler,gethandlerExecutionChain方法用于添加拦截器到执行链中。
this.getHandlerExecutionChain(handler, request);源码
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//这个是拦截器的突破口
Iterator var5 = this.adaptedInterceptors.iterator();
while(var5.hasNext()) {
HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
chain.addInterceptor(interceptor);
}
}
return chain;
} |
这个方法的作用是将hm的adaptedInterceptors添加进执行链,并将其返回。
AbstractHandlerMapping中有二个(原来有三个,其中mappedInterceptors在新的版本中被删掉了)List类型的属性:interceptors、adaptedInterceptors,它们的作用如下:
interceptors:该属性不会直接使用,而是通过initInterceptors方法按照类型分配到mappedInterceptors和adaptedInterceptors中进行使用;
adaptedInterceptors:此类Interceptor不需要匹配,在getHandler中会全部添加到HandlerExecutionChain中。
分析到这里,我们就可以发现,每一个HandlerMapping都有自己的两个List类型的拦截器属性,
而映射由@RequestMapping修饰的接口使用的HandlerMapping是RestRpcRequestMappingHandlerMapping,order为0
而ZuulHandlerMapping的order为-200,所以ZuulHandlerMapping的优先级更高,如果某个url在某个controller中通过@RequestMapping配置,同时又在ZuulHandlerMapping中配置,那么该url的请求只会进入zuul。
ZuulHandlerMapping实现了ApplicationObjectSupport,ApplicationObjectSupport的setApplicationContext方法如下
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !this.isContextRequired()) {
this.applicationContext = null;
this.messageSourceAccessor = null;
} else if (this.applicationContext == null) {
if (!this.requiredContextClass().isInstance(context)) {
throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
//初始化上下文
this.initApplicationContext(context);
} else if (this.applicationContext != context) {
throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]");
}
} |
呼应前文,这里通过实现WebMvcConfigurerAdapter,并重写addInterceptors把自定义的拦截器注册进去,通过打断点,我们发现,这里只是把自定义拦截器添加到了RestRpcRequestMappingHandlerMapping中,而没有添加到ZuulHandlerMapping中,所以,在路由到zuul的请求并没有被自定义拦截器拦截到。如果想让自定义拦截器对达到zuul的请求起作用,就需要将自定义拦截器也注册到ZuulHandlerMapping中。
3、总结
每一个请求都会通过DispatcherServlet寻找对应的HandlerMapping,而HandlerMapping会获取到对应的拦截器以及handler,在遍历HandlerMapping时是按照其order顺序进行的,ZuulHandlerMapping的优先级较高,通过ZuulHandlerMapping获取到执行链的话,就会执行其对应的Interceptors和handler,而zuul的handler就是ZuulController,ZuulController又会把请求交给ZuulServlet,ZuulServlet才是真正执行到了zuul官方文档提出的zuul生命周期。