自定义interceptor无法拦截zuul请求的问题分析

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生命周期。

 

 

 

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值