詳解SpringMVC從基礎(chǔ)到源碼
認(rèn)識SpringMVC
SpringMVC 框架是以請求為驅(qū)動,圍繞 Servlet 設(shè)計,將請求發(fā)給控制器,然后通過模型對象,分派器來展示請求結(jié)果視圖。其中核心類是 DispatcherServlet,它是一個 Servlet,頂層是實(shí)現(xiàn)的Servlet接口。

SpringMVC 處理請求過程
- 客戶端發(fā)起請求,會首先經(jīng)過前端控制器 DispatcherServlet 進(jìn)行轉(zhuǎn)發(fā),轉(zhuǎn)發(fā)到 Handler Mapping
- DispatcherServlet 從 Handler Mapping 查找處理請求的 Controller,Handler Mapping 作用就是完成 URL 到 Controller 的映射
- Controller 處理請求并返回 ModelAndView 對象,ModelAndView 是封裝結(jié)果視圖的組件
- 再將視圖結(jié)果返回給客戶端
Servlet 與 SpringMVC
SpringMVC 是在 Servlet 的基礎(chǔ)上進(jìn)行了擴(kuò)展,看看他們的繼承關(guān)系是什么樣的。
Servlet 繼承關(guān)系

SpringMVC 繼承關(guān)系

Servlet 與 SpringMVC 對比
- Servlet 需要每個請求都在 web.xml 文件中配置一個 sevlet 節(jié)點(diǎn)
- SpringMVC 的 DispatcherServlet 會攔截所有請求,讓 Controller 去處理
Structs2 與 Spring MVC
相同點(diǎn)
都是基于MVC模型的
不同點(diǎn)
- Structs2 是基于類的,一個 request 創(chuàng)建一個 action,一個action 對應(yīng)一個 request ;Servlet 是基于方法的,也就是一個 request 對應(yīng)一個方法
- Structs2 入口是 Filter;SpringMVC 入口是 Servlet
- SpringMVC 的開發(fā)速度和性能優(yōu)于 Structs2 ,流程更易理解
- SpringMVC 和 Spring 是無縫的,可以認(rèn)為 SpringMVC 是100% 零配置
SpringMVC源碼分析
1、ApplicationContext 初始化時建立所有的URL和Controller 的對應(yīng)關(guān)系
/**
* 建立當(dāng)前ApplicationContext中的所有controller和url的對應(yīng)關(guān)系
*/
protected void detectHandlers() throws BeansException {
//日志級別是否是 Debug
if (logger.isDebugEnabled()) {
//應(yīng)用上下文就是工程的訪問路徑
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
// 獲取ApplicationContext容器中所有bean的Name---Controller
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 遍歷beanNames,并找到這些bean對應(yīng)的url
for (String beanName : beanNames) {
// 獲取Controller上的所有url(類上的url+方法上的url)
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 保存urls和beanName的對應(yīng)關(guān)系,put it to Map<urls,beanName>,該方法在父類AbstractUrlHandlerMapping中實(shí)現(xiàn)
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
2、根據(jù)訪問URL找到對應(yīng) Controller 中處理請求的方法
/** 中央控制器,控制請求的轉(zhuǎn)發(fā) **/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
// 1.檢查是否是文件上傳的請求
processedRequest = checkMultipart(request);
// 2.取得處理當(dāng)前請求的controller,這里也稱為hanlder,處理器,第一個步驟的意義就在這里體現(xiàn)了.
這里并不是直接返回controller,而是返回的HandlerExecutionChain請求處理器鏈對象,該對象封裝了handler和interceptors.
mappedHandler = getHandler(processedRequest, false);
// 如果handler為空,則返回404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//3. 獲取處理request的處理器適配器handler adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 處理 last-modified 請求頭
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 4.攔截器的預(yù)處理方法
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// 5.實(shí)際的處理器處理請求,返回結(jié)果視圖對象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 結(jié)果視圖對象的處理
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// 6.攔截器的后處理方法
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// 請求成功響應(yīng)之后的方法
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
3、反射調(diào)用處理請求的方法返回結(jié)果視圖
/** 獲取處理請求的方法,執(zhí)行并返回結(jié)果視圖 **/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1.獲取方法解析器
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
// 2.解析request中的url,獲取處理request的方法
//通過 request 找 controller 中的處理方法,request的url與 controller 的url 進(jìn)行匹配
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 3.方法調(diào)用器
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
// 4.執(zhí)行方法
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
// 5.封裝結(jié)果視圖
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
到此這篇關(guān)于詳解SpringMVC從基礎(chǔ)到源碼的文章就介紹到這了,更多相關(guān)SpringMVC 源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring+SpringMVC+JDBC實(shí)現(xiàn)登錄的示例(附源碼)
- SpringMVC源碼解析之消息轉(zhuǎn)換器HttpMessageConverter
- Spring SpringMVC在啟動完成后執(zhí)行方法源碼解析
- 詳解springMVC容器加載源碼分析
- SpringMVC的源碼解析
- SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
- SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā)
- SpringMVC源碼解讀之HandlerMapping
相關(guān)文章
Spring Cloud如何切換Ribbon負(fù)載均衡模式
這篇文章主要介紹了Spring Cloud如何切換Ribbon負(fù)載均衡模式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12
如何實(shí)現(xiàn)自定義SpringBoot的Starter組件
這篇文章主要介紹了實(shí)現(xiàn)自定義SpringBoot的Starter組件的示例代碼,想要自定義starter組件,首先要了解springboot是如何加載starter的,也就是springboot的自動裝配機(jī)制原理,本文結(jié)合示例代碼詳細(xì)講解,需要的朋友可以參考下2023-02-02
SpringCloud+nacos部署在多ip環(huán)境下統(tǒng)一nacos服務(wù)注冊ip(親測有效)
在部署SpringCoud項(xiàng)目的時候分服務(wù)器部署注冊同一個nacos服務(wù),但是在服務(wù)器有多個ip存在的同時(內(nèi)外網(wǎng)),就會出現(xiàn)注冊服務(wù)ip不同的問題,導(dǎo)致一些接口無法連接訪問,經(jīng)過多次排查終于找到問題并找到解決方法,需要的朋友可以參考下2023-04-04
java實(shí)現(xiàn)字符串四則運(yùn)算公式解析工具類的方法
今天小編就為大家分享一篇java實(shí)現(xiàn)字符串四則運(yùn)算公式解析工具類的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Java為什么匿名內(nèi)部類參數(shù)引用需要用final進(jìn)行修飾?
今天小編就為大家分享一篇關(guān)于Java為什么匿名內(nèi)部類參數(shù)引用需要用final進(jìn)行修飾?,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04

