SpringMVC学习笔记
原理篇
图解MVC三大角色和三大组件作用
结构流程说明
- 用户发送请求至前端控制器
DispatcherServlet
DispatcherServlet
收到请求调用HandlerMapping
处理器映射器- 处理器映射器根据url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给
DispatcherServlet
DispatcherServlet
通过HandlerAdapter
处理器适配器调用处理器HandlerAdapter
执行处理器(handler
,也叫后端控制器)controller
执行完成返回ModelAndView
HandlerAdapter
将handler
执行结果ModelAndView
返回给DispatcherServlet
DispatcherServlet
将ModelAndView
传给ViewReslover
视图解析器ViewReslover
解析后返回具体View
对象DispatcherServlet
对View
进行渲染视图(即将模型数据填充至视图中)DispatcherServlet
响应用户
六大组件说明
- DispatcherServlet 前端控制器:用户请求到达前端控制器,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。
- HandlerMapping 处理器映射器:HandlerMapping负责根据用户请求找到Handler处理器,SpringMVC提供了不同的映射器实现不同的映射方法,如:配置文件方式、实现接口方式、注解方式等。
- Handler 处理器:Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
- HandlerAdapter 处理器适配器:通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
- ViewResolver 视图解析器:ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- View 视图:SpringMVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。一般情况下需要通过页面标签或页面模板技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。需要开发的组件有:处理器、视图
源码篇
知识储备
Servlet的生命周期
- 实例化,由web容器实例化servlet实例;
- 初始化,容器调用 init 方法:Servlet对象创建之后调用;
- 服务,客户端请求 servlet 时,容器调用 service 方法:Servlet对象被HTTP请求访问时调用;
- 销毁,结束服务,调用 destroy方法:Servlet对象销毁之前调用;
- 垃圾回收。
DispatcherServlet继承体系
InitializingBean接口介绍
- Spring有两种Bean的初始化(不是实例化)方式:
- 一种是实现InitializingBean接口;
- 一种是通过反射调用bean标签中的init-method属性指定的方法。
- 不同点:接口比配置效率高,但是配置消除了对Spring的依赖。
- InitializingBean接口为bean提供了初始化方法的方式:它只包括 afterPropertiesSet 方法;凡是实现该接口的类,在初始化bean的时候会执行该方法。
实现InitializingBean接口与在配置文件中指定init-method有什么不同?
- 系统先调用afterPropertiesSet方法,然后调用init-method中指定的方法。
这种Bean的初始化方式在spring中是怎么实现的?
- 执行时机:在创建Bean实例及设置完属性之后执行,通过查看spring加载bean的源码类(
AbstractAutowireCapableBeanFactory
类中的invokeInitMethods
)可看出其中奥妙,源码如下:
DispatcherServlet主流程
初始化流程
- 初始化入口:
GenericServlet#init(config)
// 生命周期方法之初始化方法,servlet被创建时会调用该方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
- 接下来准备调用
GenericServlet#init()
方法,不过该方法它没有实现,而是被HttpServletBean
给覆盖了,接下来直接看看HttpServletBean#init()
方法
- 接下来调用
initServletBean()
,不过该方法需要去HttpServletBean
的子类FrameworkServlet
中查看
- 方法调用到这里,我们终于知道
DispatcherServlet
初始化的主要工作是干嘛的了,就是为了创建Spring容器(其实容器内还要初始化一些组件)。接下我们看看FrameworkServlet#initWebApplicationContext()
方法
protected WebApplicationContext initWebApplicationContext() {
// 父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 子容器
WebApplicationContext wac = null;
// 省略一些判断、设置...
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
// 刷新容器中的策略
onRefresh(wac);
}
// 省略...
return wac;
}
onRefresh()
方法是初始化一些默认组件,比如HandlerMapping组件中的BeanNameURLHandlerMapping,我们进入这个方法看看:
- 看到这里,我们已经明白入门程序为什么没有配置三大组件,Spring容器中却依然有这些组件。这个地方初始化了很多默认配置,我们可以随便找个
initHandlerMapping
来了解一下它们的实现:
- 看看是如何加载默认策略的,进入
getDefaultStrategies
方法:
- 那么
defaultStrategies
集合是如何初始化的呢?
至此,DispatcherServlet
初始化工作就完成了。
- 记笔记:
GenericServlet#init(config):生命周期方法之初始化方法:Servlet被创建时会调用该方法
|--HttpServletBean#init():子类HttpServletBean覆写父类【GenericServlet】的init()空方法——模板方法模式
|--FrameworkServlet#initServletBean():子类FrameworkServlet覆写父类【HttpServletBean】的initServletBean()空方法——模板方法模式
|--FrameworkServlet#initWebApplicationContext():创建spring容器,父子容器创建
|--DispatcherServlet#onRefresh(ApplicationContext context):刷新容器【初始化一些默认组件】,子类DispatcherServlet覆写父类【FrameworkServlet】的onRefresh()空方法——模板方法模式
|--DispatcherServlet#initStrategies(context):初始化组件策略
|--DispatcherServlet#initHandlerMappings(context):以初始化处理器映射器组件为例,从spring容器中获取HandlerMapping,如果没有就走默认策略
|--DispatcherServlet#getDefaultStrategies(context, HandlerMapping.class):获取默认的组件初始化策略,其中defaultStrategies是在静态代码块中去读取DispatcherServlet.properties的内容
- 涉及到的类:
* GenericServlet
* HttpServletBean
* FrameworkServlet
* DispatcherServlet
访问处理流程
- 访问入口:
HttpServlet#service(...)
方法(该方法的request 和 response参数不是Http开头的)
- 继续跟踪到另一个
service
方法(请求和响应式Http开头的),该方法根据请求的method分别调用相应的处理器
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
- 我们主要去看看
doGet
和doPost
方法是如何处理的,由继承体系分析得知,我们应该去FrameworkServlet
类查看:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
- 到此为止,我们还没有进入
DispatcherServlet
类,所以继续去processRequest
方法看一下:
- 通过
doService
方法才正式进入到DispatcherServlet
类中去执行:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 省略...
try {
// 最核心的一个方法,处理请求分发(做调度)
doDispatch(request, response);
}
// 省略...
}
- 至此,我们终于找到了
DispatcherServlet
中最核心的一个方法:doDispatch
,进到方法里查看:
- 源码阅读到这里,最起码从主流程中我们可以得知
DispatcherServlet
是如何处理一个请求的。但是我们看到现在,还没有看出DispatcherServlet
是如何与三大组件进行联系的。所以我们要分别找到三大组件与DispatcherServlet
交互的地方。 - 处理器映射器与
DispatcherServlet
交互的地方:getHandler
方法:
- 处理器适配器与
DispatcherServlet
交互的地方:getHandlerAdapter
方法:
- 视图解析器与
DispatcherServlet
交互的代码需要多深入几层去了解,先进入processDispatchResult
方法:
- 再进入
render
方法:
- 这个
resolveViewName
方法就是视图解析器和DispatcherServlet
交互的方法:
- 到此,
DispatcherServlet
的主流程以及三大组件和它的联系,我们都已经搞清楚了。至于,三大组件的具体实现,这里暂不分析。 - 记笔记:
HttpServlet#service(req, res)
|--HttpServlet#service(req, res)
|--FrameworkServlet#doGet(req, res)
|--FrameworkServlet#processRequest(req, res)
|--FrameworkServlet#processRequest(req, res):处理请求,但是还没有真正干活
|--DispatcherServlet#doService(req, res):真正干活【很关键的一步】
|--DispatcherServlet#doDispatch(req, res):最核心的一个方法
|--DispatcherServlet#checkMultipart(req):处理含文件上传的请求
|--DispatcherServlet#getHandler(processedRequest):通过处理器映射器解析请求URL,获取对应的处理器对象
|--DispatcherServlet#getHandlerAdapter(mappedHandler.getHandler()):根据查找到的处理器对象,找到它的处理器适配器对象
|--DispatcherServlet#processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException):处理执行结果,即对ModelAndView进行处理【视图解析、视图渲染】
|--DispatcherServlet#processHandlerException(request, response, handler, exception):处理异常
|--DispatcherServlet#render(mv, request, response):视图渲染
|--DispatcherServlet#resolveViewName(viewName, mv.getModelInternal(), locale, request):根据视图名称,调用视图解析器,解析出来视图对象
- 涉及到的类:
* HttpServlet
* FrameworkServlet
* DispatcherServlet
拦截器处理流程
- 分析源码入口:
DispatcherServlet#doDispatch
方法:
preHandle
流程分析(HandlerExecutionChain类)
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取当前HandlerMapping返回的拦截器集合
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// 执行拦截器preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
// 记录最后成功执行的处理器拦截器在拦截器集合中的下标,影响postHandle和afterCompletion方法的执行顺序
this.interceptorIndex = i;
}
}
return true;
}
postHandle
流程分析(HandlerExecutionChain类)
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {// 倒序
HandlerInterceptor interceptor = interceptors[i];
// 执行postHandle方法
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
afterCompletion
流程分析(HandlerExecutionChain类)
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 边界是preHandle方法赋值的interceptorIndex,倒序执行afterCompletion方法
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
- 记笔记:
DispatcherServlet#doDispatch()
|--DispatcherServlet#applyPreHandle(..):执行处理器的preHandler方法
|--HandlerExecutionChain#applyPreHandle(..)
|--DispatcherServlet#applyPostHandle(..):执行处理器的postHandler方法
|--HandlerExecutionChain#applyPostHandle(..)
|--DispatcherServlet#processDispatchResult(..):执行处理器的afterCompletion方法
|--DispatcherServlet#triggerAfterCompletion(..):执行处理器的afterCompletion方法
|--HandlerExecutionChain#triggerAfterCompletion(..)
- 涉及到的类:
* DispatcherServlet
* HandlerExecutionChain
处理器映射器
- 主要分析注解方式的处理器映射器
RequestMappingHandlerMapping
:根据@ResquestMapping
注解查找处理器(HandlerMethod
)。
RequestMappingHandlerMapping继承体系
注册流程
- 分析入口:
RequestMappingHandlerMapping#afterPropertiesSet
方法:
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
- 继续进入父类
AbstractHandlerMethodMapping#afterPropertiesSet
方法,其中调用了initHandlerMethods
方法(定义了映射器处理器的主流程)
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
- 我们继续去看看
detectHandlerMethods
方法(核心处理方法)
- 上面的方法处理逻辑主要分为两大步骤:
- 映射Method和RequestMappingInfo的关系
- 映射关系:RequestMappingInfo与URL和HandlerMethod的关系
- 我们先看第一大步骤,也就是调用
MethodIntrospector#selectMethods
方法
- 我们去看一下
MetadataLookup
的匿名内部类实现中调用的getMappingForMethod
方法是如何实现的?需要去RequestMappingHandlerMapping
类中去查看方法:
- 至此,第一大步骤我们阅读完了,接下来去看看第二大步骤,我们看看
AbstractHandlerMethodMapping#registerHandlerMethod
方法:
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
- 接下来,我们去看看
register
方法:
- 至此处理器映射器的初始化流程就完成了。
- 记笔记:以RequestMappingHandlerMapping为例
RequestMappingHandlerMapping#afterPropertiesSet()
|--AbstractHandlerMethodMapping#afterPropertiesSet()
|--AbstractHandlerMethodMapping#initHandlerMethods()
|--AbstractHandlerMethodMapping#detectHandlerMethods(beanName):【核心的处理方法】
|--MethodIntrospector#selectMethods(userType, (MethodIntrospector.MetadataLookup<T>)method):将处理器中的方法进行过滤,映射Method和RequestMappingInfo
|--RequestMappingHandlerMapping#getMappingForMethod(Method method, Class<?> handlerType):将RequestMapping注解封装成RequestMappingInfo对象返回
|--RequestMappingHandlerMapping#createRequestMappingInfo(method):获取方法上的RequestMapping注解信息
|--RequestMappingHandlerMapping#createRequestMappingInfo(handlerType):获取类上的RequestMapping注解信息
|--RequestMappingHandlerMapping#createRequestMappingInfo:封装RequestMappingInfo对象信息
|--MethodIntrospector.MetadataLookup#inspect(Method method):返回RequestMappingInfo对象
|--AbstractHandlerMethodMapping#registerHandlerMethod(handler, invocableMethod, mapping):将handler和method封装成HandlerMethod对象,进行映射注册
|--AbstractHandlerMethodMapping#register(mapping,handler, method)
|--Map<T, HandlerMethod> mappingLookup:将HandlerMethod和HandlerMappingInfo进行映射
|--MultiValueMap<String, T> urlLookup:将URL和HandlerMappingInfo进行映射
|--handlerMethodsInitialized(getHandlerMethods()):还未实现
- 涉及到的类:
* RequestMappingHandlerMapping
* AbstractHandlerMethodMapping
* MethodIntrospector
* MethodIntrospector.MetadataLookup
处理流程
- 分析入口:
DispatcherServlet#getHandler
方法:
- 接下来我们需要进入到具体的
AbstractHandlerMapping#getHandler
方法:
- 接下来我们重点看看
AbstractHandlerMethodMapping#getHandlerInternal
方法:
- 接下来我们重点去看看
lookupHandlerMethod
方法(核心方法):
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根据查找路径(URL)去urlLookup集合中获取匹配到的RequestMappingInfo集合
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 将RequestMappingInfo对象和HandlerMethod对象进行匹配,将匹配到的信息封装到Match对象,再将Match对象放入matches集合
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 使用指定的比较器,将匹配到的Match对象进行排序
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
// 找到最优匹配项
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
// 获取次优匹配项
Match secondBestMatch = matches.get(1);
// 如果最优匹配项和次优匹配项是相同的,则报错
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回最匹配的HandlerMethod对象
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
- 其中,我们继续去看看
addMatchingMappings
方法:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
// 获取待匹配的RequestMappingInfo对象,封装RequestMapping注解信息
T match = getMatchingMapping(mapping, request);
if (match != null) {
// 将待匹配的RequestMappingInfo和HandlerMethod对象封装到Match对象中,并添加到集合
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
- 接着去
RequestMappingInfoHandlerMapping#getMatchingMapping
方法:
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
- 最后去
RequestMappingInfo#getMatchingCondition
方法:
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (methods == null || params == null || headers == null || consumes == null || produces == null) {
return null;
}
// 获取到RequestMapping注解中value属性的值
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
- 记笔记:
DispatcherServlet#getHandler(HttpServletRequest request)
|--AbstractHandlerMapping#getHandler(request):返回HandlerExecutionChain
|--AbstractHandlerMethodMapping#getHandlerInternal(request):交给具体子类【比如AbstractHandlerMethodMapping】实现,返回HandlerMethed对象
|--AbstractHandlerMethodMapping#lookupHandlerMethod:根据请求URL,获取HandlerMethod对象【核心方法】
|--AbstractHandlerMethodMapping#addMatchingMappings:将RequestMappingInfo对象和请求中的信息进行匹配,将匹配信息封装到Match对象存储到集合中
|--RequestMappingInfoHandlerMapping#getMatchingMapping:获取匹配到的RequestMappingInfo对象
|--RequestMappingInfo#getMatchingCondition
|--getHandlerExecutionChain(handler, request):将Handler和HandlerInterceptor封装成一个执行链对象
- 涉及到的类:
* DispatcherServlet
* AbstractHandlerMapping
* AbstractHandlerMethodMapping
* RequestMappingInfoHandlerMapping
* RequestMappingInfo