【Spring】spring mvc源碼分析(一)
时间是最好的老师,但遗憾的是——最后他把所有的学生都弄死了
准备花个点时间来写点关于spring mvc,内容主要有
请求如何相应的到控制器的方法
请求参数如何绑定
结果如何返回的
spring mvc基本原理
说起spring mvc, 我们有必要说下 Servlet的基本原理。
回忆下,在学习Servlet时,我们写一个自己的Servlet继承HttpServlet,并重写其中的doGet(),doPost()等方法来实现具体的业务逻辑,还可以重写init()方法进行初始化其它内容,然后配置下web.xml使得相应的请求打到我们写的Servlet上。
而spring mvc整个框架是基于单个Servlet构建起来的,在spring mvc中,这个Servlet叫做DispatcherServlet,DispatcherServlet正如它名字一样,它是一个分发请求的Servlet,只负责调度,这个Servlet拦截了所有的请求,使得请求都打到这个这个Servlet上。然后将请求分发到controller的方法,具体的业务逻辑其实是从controller的方法来实现的。
那么一个请求是如何通过DispatcherServlet分发到特定的controller方法呢?Servlet中是通过配置web.xml的请求地址与Servlet的映射关系来实现的,而spring mvc 则是通过@RequestMapping这个注解映射的;在spring mvc初始化是会将这些映射关系注册起来,形成<Key,Value>的形式,这里的Key指的是请求地址,Value为响应的方法。
本文着重讲spring mvc初始化时请求地址、响应方法的注册,以及一个请求进来时是如何打到相应的controller方法的
spring mvc的初始化
Servlet在初始化时会调用init()方法,这个init()方法最终会调用到DispatcherServlet的initStrategies()方法,该方法内容如下。
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
其中initHandlerMappings()方法就是用于初始化请求一部分。源码如下
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) {//初始化时默认会执行这一分支的逻辑 // 查找HandlerMapping接口的实现类 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { ... } }
在spring mvc 初始化时,打个断点看下handlerMappings这个属性,里面包含了几个HandlerMapping的实现;
RequestMappingHandlerMapping这个是我们在controller上面加@RequestMapping注解使用的到的HandlerMapping实现类,如下图:
this.handlerMappings.png
我们们再进一步理解下RequestMappingHandlerMapping,看下它是如何处理@RequestMapping这个注解,以实现<请求信息,响应方法>的注册的,它的继承关系其实有些复杂,我们只给出了主干部分结构图,如下:
RequestMappingHandlerMapping.png
重点看RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping,里面有个属性,源码如下:
private final MappingRegistry mappingRegistry = new MappingRegistry();
MappingRegistry的定义
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>(); }
MappingRegistration的定义
private static class MappingRegistration<T> { private final T mapping; //T为RequestMappingInfo,封装了请求信息,包含请求路径,请求头等 private final HandlerMethod handlerMethod;//请求对应的处理方法 }
到此,我们已经大概明白了,spring mvc其实是通过MappingRegistration这个类去封装请求信息和响应方法的,当一个请求进来时,能拿到请求的路径时,其实也就能知道由哪个方法去处理请求了。
在这里还需要注意的是mappingRegistry这个属性的初始化并不是在Servlet初始化进行了,它是依赖于spring容器的,回头看下前面的类图,AbstractHandlerMethodMapping这个类它实现了一个InitializingBean接口,这个接口在spring容器初始化时调用,初始化时最终会调用到initHandlerMethods()方法。方法内容如下:
protected void initHandlerMethods() { //获取到所有bean名称 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; beanType = getApplicationContext().getType(beanName); //如果该bean带有@Controller和@RequestMapping注解,则该bean被封装成HandlerMethod注册到mappingRegistry if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } }
spring mvc请求的分发
前文有提到,DispatcherServlet这是一个分发请求的Servlet,那么它是如何实现的呢?
DispatcherServlet中有个doDispatch()方法,它是在Servlet处理请求是调用的,doDispatch()的方法很长,这次我们只看下如何将请求分发到相应的controller方法中。源码精简后如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; try { // 根据请求来获取相应的HandlerExecutionChain,这个类是响应请求方法的一个封装类 mappedHandler = getHandler(processedRequest); // 获取请求适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 调用响应方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } catch (Exception ex) { ... } }
getHandler()方法:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //获取响应方法 Object handler = getHandlerInternal(request); //将响应方法封装成HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); return executionChain; }
getHandlerInternal()方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //根据请求生成查找的路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { //根据查找路径找到响应的请求方法,具体如何查找就不展开了 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
作者:eejron
链接:https://www.jianshu.com/p/f647e5c3d0d1
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章