记一次HTTP请求的完整流转 从Tomcat到Spring MVC

Spring boot 一次详细的http请求过程。

1,我们知道的是spring boot内嵌一个tomcat,这个tomcat负责请求的连接处理,并且将其转化为request和response,然后我们所有的操作都是在处理这个request和response,然后tomcat再通过socket写回给客户端。这里nio模型的中心,也就是tomcat负责事件处理的伪码

public void run() {
    while (true) {
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator != null && iterator.hasNext()) {
            SelectionKey sk = iterator.next();
            NioSocketWrapper attachment = (NioSocketWrapper) sk.attachment();
            iterator.remove();
            if (attachment == null) {
                processKey(sk, attachment);
            }
        }
    }//while
}

2,然后这里的processKey就会调用方法processSocket,提交为一个任务,到另一个线程去跑。也就是说这里是一个纯nio的内容,有任务会丢到另一个线程去跑,这个是一个死循环,直到容器关闭。

3,在另一个线程中,会使用Http11Processor来处理这个SocketWrapperBase ,但是看代码的结论貌似是一个socket连接对应一个processor,因为做了一个缓存connect,要注意,这里实际上已经创建了request和response,但是都是默认值,org.apache.coyote.http11.Http11Processor#service在这个方法中,会去使用这个SocketWrapperBase对response和request进行赋值。

4,然后再使用CoyoteAdapter处理request和response,getAdapter().service(request, response);然后就是层层调用:

StandardEngineValve --> ErrorReportValve --> StandardHostValve --> StandardPipeline 直到StandardContextValveàStandardWrapperValve(这里是Valve而不是value,发音为`哇呜,这里的哇重音。),然后终于到了tomcat和spring的分水岭StandardWrapperValve这个类就是分水岭因为这个类持有一个servlet,实例化的时候会被实例化为dispatchservlet,而dispatchservlet就是spring中的概念了。

这里要注意的方法是org.springframework.web.servlet.DispatcherServlet#initStrategies,也就是dispatchServlet的初始化策略的方法。

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

这里比较重要的应该是初始化方法映射和方法适配器。

5,创建了dispatchservlet后,会创建执行链

ApplicationFilterChain filterChain =  ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

要注意的是,这里会拿到所有的filter,也包含我们实现的filter接口的过滤器,有意思的一点是,并不是每个filter都会加到这个filterchain中去,只有哪些url pattern匹配的filter才会加入到ApplicationFilterChain来。

6,然后调用 filterChain.doFilter(),在过滤链的最后,会调用servlet的service方法。处理这个request和response,这里又出现了层层调用,最后还是调用到DispatcherServlet的doDispatch。

7,回到doDispatch,在这里会拿到对应request的HandlerAdapter,在HandlerAdapter.handle之前和之后,分别会调用,HandlerExecutionChain. applyPreHandle和applyPostHandle和triggerAfterCompletion,分别对应interceptor的三个方法,也就是说,这里会调用实现了HandlerInterceptor的所有interceptor,前提是要实现WebMvcConfigurer并复写addInterceptors(InterceptorRegistry registry),(ps,这里有个关键字default,如果加了这个关键字,实现接口的时候不必每个都实现,这个小点是我刚刚发现的。以前我还不知道可以这样。)

这里和filter不同的是,fitler是有 urlpattern的,所以不是所有的fitlter都会执行,但是这里的interceptor是都会执行的,当然了,如果某一个filter或者intercepor如果不通过,就会直接返回,不会再向下走了。

8,然后就到了RequestMappingHandlerAdapter这里会创建一个ServletInvocableHandlerMethod 对象,然后为这个对象设置一堆属性,例如HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler等等,然后再调用ServletInvocableHandlerMethod的invokeAndHandle这个方法,在这个方法中,会首先利用参数解析器从request中拿到参数,然后再调用反射,直接执行对应方法。拿到结果。然后在用HandlerMethodReturnValueHandler对结果进行处理并返回。这里要注意的点是,这里的返回值处理器是我们自己实现接口HandlerMethodReturnValueHandler,并配置(也就是实现WebMvcConfigurer接口,并且覆写addReturnValueHandlers方法实现的。)才会生效,并且这里的处理过程是拿到第一个可以处理的handler,然后处理并返回。

至此,终于完成了一次比较完整的http请求的访问。

对应的时序图:(很失望的是,这里会对图片压缩。visio的矢量图就变成了标量图。)

总结小点

1,我发现大牛总是乐意定义一套流程,然后某个可以自定义的方法定义为abstract的,然后自己去实现这个方法,也就是可以让流程跑起来,然后再DIY不同的实现。这是很厉害的。像是上边的从CoyoteAdapter到StandardWrapperValve这里的层层调用,以及从servlet到dispatchServlet的层层调用都使用了这种方法。

2,所以这里有一个问题,就是如果我们想自定义结果解析就会出问题,(也就是实现HandlerMethodReturnValueHandler这个接口,统一回包格式的功能)因为默认的有个RequestResponseBodyMethodProcessor,这个是处理model的也就是会将model转化为view,但是这个优先级比较高,并且结果处理总是使用第一个支持的处理器去执行,因此,我们自己定义的返回值处理器是没法儿执行的)

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();
        if (this.argumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();// !!!这里调用下面的代码!!!
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
        
        // 省略代码
        
        {
            protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {//!!!调用这里!!!
                List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
    
                // Single-purpose return value types
                handlers.add(new ModelAndViewMethodReturnValueHandler());
                handlers.add(new ModelMethodProcessor());
                handlers.add(new ViewMethodReturnValueHandler());
                handlers.add(new HttpEntityMethodProcessor(
                        getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
    
                // Annotation-based return value types
                handlers.add(new ModelAttributeMethodProcessor(false));
                handlers.add(new RequestResponseBodyMethodProcessor(//这里的优先级很高,自定义的会放在这个list的尾巴上。导致没法儿执行
                        getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
    
                // Multi-purpose return value types
                handlers.add(new ViewNameMethodReturnValueHandler());
                handlers.add(new MapMethodProcessor());
    
                // Custom return value types
                if (getCustomReturnValueHandlers() != null) {
                    handlers.addAll(getCustomReturnValueHandlers());
                }
    
                // Catch-all
                handlers.add(new ModelAttributeMethodProcessor(true));
    
                return handlers;
            }
        }
    }
}

对于这个问题,我目前也没有什么好的解决办法,可能的solution是继承RequestMappingHandlerAdapter,把原生的顺序改掉,也就是把自定义的放在list的首部。希望看见的大佬可以帮我想想怎么解,谢谢啦!!!

 

发布了29 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u012803274/article/details/104723613