时间是最好的老师,但遗憾的是——最后他把所有的学生都弄死了
准备花个点时间来写点关于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初始化是会将这些映射关系注册起来,形成
本文着重讲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 matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
...
}
}
在spring mvc 初始化时,打个断点看下handlerMappings这个属性,里面包含了几个HandlerMapping的实现;
RequestMappingHandlerMapping这个是我们在controller上面加@RequestMapping注解使用的到的HandlerMapping实现类,如下图:
我们们再进一步理解下RequestMappingHandlerMapping,看下它是如何处理@RequestMapping这个注解,以实现<请求信息,响应方法>的注册的,它的继承关系其实有些复杂,我们只给出了主干部分结构图,如下:
重点看RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping,里面有个属性,源码如下:
private final MappingRegistry mappingRegistry = new MappingRegistry();
MappingRegistry的定义
class MappingRegistry {
private final Map> registry = new HashMap>();
}
MappingRegistration的定义
private static class MappingRegistration {
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();
}
}
在spring mvc中,所以的请求及响应都是围绕着DispatcherServlet这个类做文章的,而这个类其实就是一个自定义的Servlet,就是那么一个简单的Servlet配合上spring容器也并发出强大的威力。
Spring 相关内容:
- Ioc容器的实现(一)-- BeanFactory
- Ioc容器的实现(二)-- ApplicationContext