最近在研究springmvc的工作原理,看了源码顺手笔记下来,方便自己以后学习,在看源码前,先看下几个核心类:
1.HttpServletBean
主要做一些初始化的工作,将web.xml中配置的参数设置到Servlet中。比如servlet标签的子标签init-param标签中配置的参数。
2.FrameworkServlet
将Servlet与Spring容器上下文关联。其实也就是初始化FrameworkServlet的属性webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,既web.xml中配置的ContextLoaderListener监听器初始化的容器上下文。
3.DispatcherServlet
初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。
我们主要从DispatcherServlet 的doDispatch方法研究。
当我们请求到达时,会被DispatcherServlet 拦截到,根据方法类型去下发请求,最后会走到doDispatch方法。
首先会到getHandler方法中获取HandlerExecutionChain的处理执行链
点进getHandler方法后,可以看到最后会返回一个HandlerExecutionChain
其中handlerMappings是我们的处理器映射器,里面包含了5个,包括我们经常使用的RequestMappingHandlerMapping,这个方法会遍历所有的处理器映射器,然后依次执行处理器映射器的getHandler方法
然后会进入AbstractHandlerMapping类中的getHandler方法
这个方法第一行getHandlerInternal方法获取内部处理的handler,点进去
会执行到RequestMappingInfoHandlerMapping类中的getHandlerInternal方法
执行父类AbstractHandlerMethodMapping的getHandlerInternal方法,查找给定请求的处理程序方法。
然后会执行initLookupPath方法,初始化查找路径
会拿到我们请求的uri路径。执行lookupHandlerMethod方法得到处理该路径的方法。
getMappingsByDirectPath方法从mappingRegistry中获取到所有匹配的请求路径
addMatchingMappings方法会匹配真正适合的,比如get和post的请求路径一样,该方法会根据请求方式拿到合适的那个。
如果获取到多个,会进行判断,最终会执行bestMatch.getHandlerMethod()的方法,获取能处理当前请求的最终执行方法
会获取到执行请求的全路径名和方法名
getHandlerInternal方法最终会将请求包装成一个HandlerMethod返回,里面包含了bean的描述,beanFactory,方法,参数等信息。
然后在doDispatch方法中就会得到一个HandlerExecutionChain处理器执行链
根据得到的处理器通过getHandlerAdapter方法获取对应的处理器适配器
spring在启动时会初始化4个处理器适配器,依次遍历调用adapter.supports(handler)看是否能够处理我们的请求,如果为true就直接返回适配器
真正执行我们的程序方法是在ha.handle这个方法中调用的
会执行RequestMappingHandlerAdapter的handleInternal方法,然后会调用invokeHandlerMethod方法
在这个方法中我们可以看到setHandlerMethodArgumentResolvers和setHandlerMethodReturnValueHandlers两个方法,他们分别是设置参数解析器和结果解析器
其中参数解析器有27个,就是我们平时在方法参数上添加的一些参数注解,这里会设置进去,请求到达的时候依次执行这些参数解析器进行比对
结果解析器有15个,返回的时候会依次执行这些解析器,匹配我们的返回结果,比如我们标注了@ResponseBody注解最终就会由RequestResponseBodyMethodProcessor处理
执行invocableMethod.invokeAndHandle方法
会调用InvocableHandlerMethod类中的invokeForRequest方法,该方法会调用getMethodArgumentValues方法来获取真正的参数解析器
获取方法的参数,遍历参数的个数,调用resolvers.supportsParameter,看那个参数解析器能支持此参数的解析
这边会获得所有的参数解析器,遍历执行,最终得到可以处理该参数的参数解析器,房后放入缓存argumentResolverCache中,方便下次调用
resolvers.resolveArgument方法解析参数的值并填充参数
依次填充每个参数,完成后会返回一个参数的数组,此时已经将参数绑定了
绑定完参数后会调用this.doInvoke(args)方法
该方法最终会调用method.invoke(this.getBean(), args)方法通过反射执行我们真正在Controller层写的目标方法
拿到我们目标方法的返回值后会调用returnValueHandlers.handleReturnValue来处理我们的返回值,就是刚才说的15个结果解析器
调用selectHandler方法查询合适的处理器
遍历所有的结果处理器,调用handler.supportsReturnType(returnType)看每个处理器是否支持当前的结果处理,最终会返回一个HandlerMethodReturnValueHandler处理器
调用handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest)方法将数据协会浏览器,此时我们就可以在浏览器看到我们的接口结果了
整体流程大致就是这样,文档是我自己按照源码一步一步走下来的,感兴趣的朋友也可以自己按照文档走一遍,有问题可以在下方留言。
原创不易,谢谢大家。
多多点赞~~~~