Spring boot 一次详细的http请求过程。
1,我们知道的是spring boot内嵌一个tomcat,这个tomcat负责请求的连接处理,并且将其转化为request和response,然后我们所有的操作都是在处理这个request和response,然后tomcat再通过socket写回给客户端。这里nio模型的中心,也就是tomcat负责事件处理的伪码
public void run() {
while (true) {
Iterator 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.
* 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 resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List handlers = getDefaultReturnValueHandlers();// !!!这里调用下面的代码!!!
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
// 省略代码
{
protected List getDefaultReturnValueHandlers() {//!!!调用这里!!!
List 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的首部。希望看见的大佬可以帮我想想怎么解,谢谢啦!!!