<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>mainGet.jsp</title>
</head>
<body>
<h2>main......page.....get......</h2>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>mainPost.jsp</title>
</head>
<body>
<h2>main......page.....post......</h2>
</body>
</html>
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class ReqAction {
@RequestMapping(value="/req", method=RequestMethod.GET)
public String reqGet(){
System.out.println("獲取到get請求,服務器被訪問......");
return "mainGet";
}
@RequestMapping(value="/req", method=RequestMethod.POST)
public String reqPost(){
System.out.println("獲取到post請求,服務器被訪問......");
return "mainPost";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index.jsp</title>
</head>
<body>
<h2>分別用get和post方式訪問服務器</h2>
<form action="${pageContext.request.contextPath}/req.action" method="get/post">
<input type="submit" value="get/post方式提交">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>dataSubmit.jsp</title>
</head>
<body>
<h2>dataSubmit.........page</h2>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index.jsp</title>
</head>
<body>
<h2>SpringMVC中獲取前端提交數據的5種方式</h2>
<hr>
<h2>方式1:單個數據的獲取方式</h2>
<form action="${pageContext.request.contextPath}/dataSubmit/submit01.action" method="get">
姓名:<input type="text" name="name"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<h2>方式2:封裝成實體類進行獲取</h2>
<form action="${pageContext.request.contextPath}/dataSubmit/submit02.action" method="get">
姓名:<input type="text" name="name"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<h2>方式3:動態占位符獲取提交的數據(只可以用在超鏈接或地址欄)</h2>
<!-- 攜帶的數據放在.action之前,用斜杠分隔開-->
<a href="${pageContext.request.contextPath}/dataSubmit/submit03/xun/22.action">動態占位符獲取提交數據</a>
<h2>方式4:參數名稱不一致時接收數據</h2>
<form action="${pageContext.request.contextPath}/dataSubmit/submit04.action" method="get">
姓名:<input type="text" name="name"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
<h2>方式5:手工獲取前端提交的數據</h2>
<form action="${pageContext.request.contextPath}/dataSubmit/submit05.action" method="get">
姓名:<input type="text" name="name"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
package com.example.controller;
import com.example.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/dataSubmit")
public class DataSubmit {
//直接注入獲取
@RequestMapping("/submit01")
public String dataSubmit01(String name, int age){//前端提交的數據,通過SpringMVC框架依次分別注入到目標方法的參數中,且自動完成了類型轉換
System.out.println("姓名: " + name + " 年齡: " + age);
return "dataSubmit";
}
//封裝成實體類來獲取
@RequestMapping("/submit02")
public String dataSubmit02(User user){//SpringMVC自動創建實體類對象,并將前端提交的數據,注入到User實體類中的對應屬性中
System.out.println(user);
return "dataSubmit";
}
//動態占位符獲取數據
@RequestMapping("/submit03/{name}/{age}")//目標路徑后用大括號接住前端隨著地址攜帶來的數據,括號用斜杠分割開,括號內名稱和@PathVariable后面的注解名一致,將攜帶的數據注入給目標方法中的對應變量
public String dataSubmit03(
@PathVariable("name")
String name,
@PathVariable("age")
int age){
System.out.println("姓名: " + name + " 年齡: " + age);
return "dataSubmit";
}
//參數名稱不一致時獲取數據
@RequestMapping("/submit04")
public String dataSubmit04(
@RequestParam("name")//若前端數據名和后端變量名不一致,則可以將@RequestParam注解名和前端傳來的數據名稱保持一致,這樣將前端數據傳給注解標識的變量
String uname,
@RequestParam("age")
int uage){
System.out.println("姓名: " + uname + " 年齡: " + uage);
return "dataSubmit";
}
//手工獲取前端提交的數據
@RequestMapping("/submit05")
public String dataSubmit05(HttpServletRequest request){//就是傳統servlet開發時獲取前端數據的方式,只不過這里HttpServletRequest實例對象由SpringMVC框架自動創建
String name=request.getParameter("name");
int age=Integer.parseInt(request.getParameter("age"));
System.out.println("姓名: " + name + " 年齡: " + age);
return "dataSubmit";
}
}
言
學了一遍SpringMVC以后,想著做一個總結,復習一下。復習寫下面的總結的時候才發現,其實自己學得并不徹底、牢固、也沒有學全,視頻跟書本是要結合起來一起,每一位老師的視頻可能提到的東西都不一致,也導致也不是很全面,書本上會講的筆記系統、全面。同時我自己也是一個初學者,下面總結的可能并不完善、正確,希望看到的大神給我指出,在此非常感謝。
目錄
Spring Web MVC (下文簡稱為 SpringMVC )是 Spring 提供 Web 應用的框架設計,屬于表現層的框架。SpringMVC是Spring框架的一部分。
Spring框架包括大致六大模塊,核心容器(Core Container)、AOP和設備支持、數據訪問及集成、Web、報文發送、Test
圖片來源于Spring官網5.0.0.M5:
? https://docs.spring.io/spring-framework/docs/5.0.0.M5/spring-framework-reference/html/overview.html#overview-modules
對于Spring5模塊圖,有2點疑問:
1、不清楚為什么在Spring官網上5.0版本以后,Release版(穩定版)的都未找到模塊圖,但是在M(里程碑版)版找到 了,如果有人在5.0以后的Release版(穩定版)找到,麻煩給我留個言,謝謝。
2、在其他博文中看到Spring5模塊結構圖是這樣的:
挺奇怪這個圖是哪里來的?(路過的大神請指點)
對于問題2,我在Spring5.2.13.RELEASE GA中,找到了如下所示信息:
拷貝以上信息:
Spring Framework Documentation
Version 5.2.13.RELEASE
What’s New, Upgrade Notes, Supported Versions, and other topics, independent of release cadence, are maintained externally on the project’s Github Wiki.
Overview | history, design philosophy, feedback, getting started. |
Core | IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP. |
Testing | Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient. |
Data Access | Transactions, DAO Support, JDBC, O/R Mapping, XML Marshalling. |
Web Servlet | Spring MVC, WebSocket, SockJS, STOMP Messaging. |
Web Reactive | Spring WebFlux, WebClient, WebSocket. |
Integration | Remoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching. |
Languages | Kotlin, Groovy, Dynamic Languages. |
按照以上信息的Web Servlet、Web Reactive已經是分屬于不同的模塊了。
Spring官方文檔:https://spring.io/projects/spring-framework#learn/
上面提到了Spring有不同的版本,在此記錄一下各個版本的意義。
描述方式 | 說明 | 含義 |
Snapshot | 快照版 | 尚不穩定,仍處于開發中的版本 |
Release | 穩定版 | 功能相對穩定,可以對外發行,但有時間限制 |
GA | 正式版 | 代表廣泛可用的穩定版(General Availability) |
M | 里程碑版 | (M是Milestone的意思)具有一些全新的功能或是有意義的版本 |
RC | 終測版 | Release Candidate(最終測試),即將作為正式版發布 |
SpringMVC執行流程圖
圖片來源:三、引用參考資料
(1) 處理器映射器:springmvc框架中的一種對象,框架把實現了HandlerMapping接口的類都叫做映射器(多個);
(2) 處理器映射器作用:根據請求,從springmvc容器對象中獲取處理器對象(MyController controller=ctx.getBean("some")
(3) 框架把找到的處理器對象放到一個叫做處理器執行鏈(HandlerExecutionChain)的類保存
(4) HandlerExecutionchain:類中保存著
?a:處理器對象(MyController);
?b:項目中的所有的攔截器List
(5) 方法調用:HandlerExecutionChain mappedHandler - getHandler (processedRequest);
(1) HandlerExecutionChain執行鏈找到對應的處理器映射器HandlerAdapter。
(2) 處理器適配器:springmvc框架中的對象,需要實現HandlerAdapter接口,
(3) 處理器適配器作用:執行處理器方法(調用MyController.doSome()得到返回值ModelAndView )
(4) 前端控制器中調用適配器:HandlerAdapter ha=getHandlerAdapter (mappedHandler.getHandler());
(5) 執行處理器方法:mv=ha.handle (processedRequest, response, mappedHandler.getHandler());
第08說明:
(1) 視圖解析器:springmvc中的對象,需要實現ViewResoler接口(可以有多個)
(2) 視圖解析器作用:組成視圖完整路徑,使用前綴,后綴。并創建View對象。
(3) view是一個接口,表示視圖的,在框架中jsp,htm1不是string表示,而是使用view和他的實現類表示視圖。
InternalResourceview:視圖類,表示jsp文件,視圖解析器會創建InternalResourceView類對象。 這個對象的里面,有一個屬性url-/WEB-INF/view/show.jsp
綜上所述,總結下SpringMVC的詳細流程圖:
圖片來源:三、引用參考資料
以下源碼來源jar包:spring-webmvc-5.25.RELEASE.jar
? ApplicationContext初始化入口類:ApplicationObjectSupport的setApplicationContext方法,setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子類AbstractDetectingUrlHandlerMapping實現了該方法。
類圖:
UML圖:
? RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping來定義controller.
初始化時,3個類的大致分工如下:
? 從上面的流程圖可以看到前端控制器(中央處理器)DistepcherServlet是SpringMVC核心,查看DistepcherServlet類的繼承情況。
UML圖:

從繼承關系看出:
? DistepcherServlet ---> FrameworkServlet ---> HttpServletBean---> HttpServlet
? 那就說明DistepcherServlet 類也是一個Servlet類,那最終核心的方法就是service()方法,即Servlet的核心方法。
? 那就找service()方法,在DistepcherServlet中沒有servic()方法,在父類FrameworkServlet有service()方法,源碼如下:
來源:
org.springframework.web.servlet.FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod=HttpMethod.resolve(request.getMethod());
if (httpMethod==HttpMethod.PATCH || httpMethod==null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
可以看到:
FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)拿到request請求,判斷當前請求是否是PATCH請求,不是的就調用父類的servic()方法,調用父類中的service方法就是去調用該類中doPost(),doGet()方法,根據不同的請求方式然后走doPost()或者doGet(),調用中以doGet()為例,
FrameworkServlet類的doGet()源碼:
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
? doGet()又調用FrameworkServlet類中的processRequest(request, response);
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime=System.currentTimeMillis();
Throwable failureCause=null;
LocaleContext previousLocaleContext=LocaleContextHolder.getLocaleContext();
LocaleContext localeContext=buildLocaleContext(request);
RequestAttributes previousAttributes=RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes=buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause=ex;
throw ex;
}
catch (Throwable ex) {
failureCause=ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes !=null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
? processRequest(request, response)方法中最關鍵的又調用了doService(request, response);查看FrameworkServlet類中的doService(request, response),或者是調試跟蹤可知,doService(request, response)由子類DispatcherServlet實現。
源碼來源:
org.springframework.web.servlet.FrameworkServlet.doService(HttpServletRequest request, HttpServletResponse response)
/**
* Subclasses must implement this method to do the work of request handling,
* receiving a centralized callback for GET, POST, PUT and DELETE.
* <p>The contract is essentially the same as that for the commonly overridden
* {@code doGet} or {@code doPost} methods of HttpServlet.
* <p>This class intercepts calls to ensure that exception handling and
* event publication takes place.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
* @see javax.servlet.http.HttpServlet#doGet
* @see javax.servlet.http.HttpServlet#doPost
*/
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
? 查看DispatcherServlet中的doService(HttpServletRequest request, HttpServletResponse response)方法
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot=null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot=new HashMap<>();
Enumeration<?> attrNames=request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName=(String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager !=null) {
FlashMap inputFlashMap=this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap !=null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot !=null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
? DispatcherServlet的doService()方法中最終調用doDispatch(request, response),查看源碼如下:
org.springframework.web.servlet.DispatcherServlet.doDispatch()
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest=request;
HandlerExecutionChain mappedHandler=null;
boolean multipartRequestParsed=false;
WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv=null;
Exception dispatchException=null;
try {
// 文件上傳相關,判斷是不是二進制請求
processedRequest=checkMultipart(request);
multipartRequestParsed=(processedRequest !=request);
// 取得處理當前請求的controller,這里也稱為hanlder處理器,第一個步驟的意義就在這里體現了.這里并不是直接返回controller,而是返回的HandlerExecutionChain請求處理器鏈對象,該對象封裝了handler和攔截器interceptors.
// Determine handler for the current request.
mappedHandler=getHandler(processedRequest);
// 如果handler為空,則返回404
if (mappedHandler==null) {
noHandlerFound(processedRequest, response);
return;
}
//3. 獲取處理request的處理器適配器HandlerAdapter
// Determine handler adapter for the current request.
HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method=request.getMethod();
boolean isGet="GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified=ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//處理器適配器執行之前,檢查攔截器的方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//處理器適配器根據找到,執行handler,返回ModelAndView
// Actually invoke the handler.
mv=ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException=ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException=new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler !=null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
? 可以看出doDispatch()就是SpringMVC的核心代碼了,分析doDispatch():
? 首先看下處理器映射器HandlerMapping類圖:
doDispatch()關鍵代碼:
HandlerExecutionChain mappedHandler=null;
mappedHandler=getHandler(processedRequest);
? mappedHandler是一個執行鏈HandlerExecutionChain 對象,這里封裝了handler和攔截器interceptors,getHandler(processedRequest)方法就是從處理器映射器HandlerMapping中找到url和controller的對應關系,并返回給前端控制器DispatchServlet。
查看getHandler(processedRequest);源碼:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings !=null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler=mapping.getHandler(request);
if (handler !=null) {
return handler;
}
}
}
return null;
}
調試代碼如下:
從代碼調試中可以看到handlerMapping中有三個對象:
this.handlerMappings={ArrayList@4662} size=3
0={BeanNameUrlHandlerMapping@4791}
1={RequestMappingHandlerMapping@4792}
2={RouterFunctionMapping@4793}
接著看getHandler(HttpServletRequest request)方法,先遍歷HandlerMappers,查找控制器找到之后就返回執行鏈HandlerExecutionChain類型的Handler。
可以看到返回的Handler中,拿到的就是我們自己編碼的Controller類,以及攔截器(演示項目中未編寫,所以調試匯總返回的Handler最后是0 interceptors)
HandlerExecutionChain with [com.bjpowernode.controller.MyController#doSome()] and 0 interceptors
將正在調試的idea打開自己編寫的Controller來對照,發現一致:
doDispatch()里面的關鍵代碼:
HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler());
源碼如下:
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters !=null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
為什么還要獲取處理器適配器HandlerAdapter:與獲取處理器映射器HandlerMapping一樣,Spring提供了不通的處理器適配器。
調試如下:
查看DEBUG調試模式中getHandlerAdapter()方法在中的:
handler、adapter、this.handlerAdapters
以下是拷貝的結果:
handler
handler={HandlerMethod@4792} "com.bjpowernode.controller.MyController#doSome()"
logger={LogAdapter$JavaUtilLog@4858}
bean={MyController@4859}
beanFactory={DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
beanType={Class@3782} "class com.bjpowernode.controller.MyController"
method={Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
bridgedMethod={Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
parameters={MethodParameter[0]@4861}
responseStatus=null
responseStatusReason=null
resolvedFromHandlerMethod={HandlerMethod@4863} "com.bjpowernode.controller.MyController#doSome()"
interfaceParameterAnnotations=null
description="com.bjpowernode.controller.MyController#doSome()"
adapter
adapter={RequestMappingHandlerAdapter@4827}
customArgumentResolvers=null
argumentResolvers={HandlerMethodArgumentResolverComposite@4833}
initBinderArgumentResolvers={HandlerMethodArgumentResolverComposite@4834}
customReturnValueHandlers=null
returnValueHandlers={HandlerMethodReturnValueHandlerComposite@4835}
modelAndViewResolvers=null
contentNegotiationManager={ContentNegotiationManager@4836}
messageConverters={ArrayList@4837} size=4
requestResponseBodyAdvice={ArrayList@4838} size=0
webBindingInitializer=null
taskExecutor={SimpleAsyncTaskExecutor@4839}
asyncRequestTimeout=null
callableInterceptors={CallableProcessingInterceptor[0]@4840}
deferredResultInterceptors={DeferredResultProcessingInterceptor[0]@4842}
reactiveAdapterRegistry={ReactiveAdapterRegistry@4844}
ignoreDefaultModelOnRedirect=false
cacheSecondsForSessionAttributeHandlers=0
synchronizeOnSession=false
sessionAttributeStore={DefaultSessionAttributeStore@4845}
parameterNameDiscoverer={DefaultParameterNameDiscoverer@4846}
beanFactory={DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
sessionAttributesHandlerCache={ConcurrentHashMap@4848} size=0
initBinderCache={ConcurrentHashMap@4849} size=0
initBinderAdviceCache={LinkedHashMap@4850} size=0
modelAttributeCache={ConcurrentHashMap@4851} size=0
modelAttributeAdviceCache={LinkedHashMap@4852} size=0
order=2147483647
supportedMethods=null
allowHeader="GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"
requireSession=false
cacheControl=null
cacheSeconds=-1
varyByRequestHeaders=null
useExpiresHeader=false
useCacheControlHeader=true
useCacheControlNoStore=true
alwaysMustRevalidate=false
servletContext={ApplicationContextFacade@4754}
logger={LogAdapter$JavaUtilLog@4854}
applicationContext={XmlWebApplicationContext@4665} "WebApplicationContext for namespace 'myweb-servlet', started on Tue Mar 02 23:25:35 CST 2021"
messageSourceAccessor={MessageSourceAccessor@4855}
this.handlerAdapters
this.handlerAdapters={ArrayList@4658} size=4
0={HttpRequestHandlerAdapter@4810}
1={SimpleControllerHandlerAdapter@4820} //XML方式
2={RequestMappingHandlerAdapter@4827} //注解方式
3={HandlerFunctionAdapter@4832}
可以看到找到4個處理器適配器。通過DEBUG模式可以看到,此次取到的處理器適配器HandlerAdapter是:RequestMappingHandlerAdapter
ha={RequestMappingHandlerAdapter@4827}
doDispatch()中的關鍵代碼:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
applyPreHandle(processedRequest, response)源碼:
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors=getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i=0; i < interceptors.length; i++) {
HandlerInterceptor interceptor=interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex=i;
}
}
return true;
}
doDispatch()中的關鍵代碼:
mv=ha.handle(processedRequest, response, mappedHandler.getHandler());
DEBUG模式調試,是調到了:
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
源碼如下:
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
再往下看handleInternal(request, response, (HandlerMethod) handler)方法,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session=request.getSession(false);
if (session !=null) {
Object mutex=WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav=invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav=invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav=invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
注意,handleInternal(request, response, (HandlerMethod) handler)方法的返回值是ModelAndView ,這里就完成了處理器適配器HandlerAdapter執行Handler(Controller)并將結果ModelAndView返回給前端控制器DistepchServlet
??接上2.3:前端控制器DistepchServlet接收到處理器適配器HandlerAdapter返回的ModelAndView以后,這里分2種情況:
總結一下:
視圖解析器ViewResolver接口主要作用是解析前端控制器DispatcherServlet傳遞的邏輯視圖名,并將解析結果的真正的視圖對象View傳回給前端控制器DispatcherServlet
ViewResolverd的實現類:
ViewResolver的UML:
.png)
視圖類型 | 簡介 | |
URL視圖資源圖 | InternalResourceView | 將JSP或其他資源封裝成一個視圖。被視圖解析器InternalResourceViewResolver默認使用。 |
JstlView | InternalResourceView的子類。如果JSP中使用了JSTL的國際化標簽,就需要使用該視圖類。 | |
文檔視圖 | AbstractExcelView | Excel文檔視圖的抽象類。 |
AbstractPdfView | PDF文檔視圖的抽象類 | |
報表視圖 | ConfigurableJasperReportsView | 常用的JasperReports報表視圖 |
JasperReportsHtmlView | ||
JasperReportsPdfView | ||
JasperReportsXlsView | ||
JSON視圖 | MappingJackson2JsonView | 將數據通過Jackson框架的ObjectMapper對象,以JSON方式輸出 |
DispatcherServlet.properties文件是在SpringMVC架包中:
DispatcherServlet.properties內容:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
SpringMVC為什么能加載不同處理器映射器HandlerMapping、處理器適配器handlerAdapter,就是因為框架配置了這個DispatcherServlet.properties文件。
轉載于:https://www.cnblogs.com/chuchq/p/14489716.html
pring MVC框架中,View與ModelAndView是緊密關聯的核心組件,它們分別代表了視圖層的抽象概念與控制器返回結果的封裝對象,共同實現了MVC模式中的視圖渲染與模型數據傳遞。以下對這兩個組件進行詳述,并輔以代碼示例。
View是Spring MVC中對具體視圖技術(如JSP、Thymeleaf、FreeMarker等)的抽象,它負責將模型數據渲染成最終發送給客戶端的HTTP響應內容。View接口定義了視圖的基本行為:
具體的視圖技術實現需要提供符合上述接口的類,例如JSP對應的InternalResourceView、Thymeleaf對應的ThymeleafView等。這些視圖類通常會解析模型數據,并利用相應的模板引擎或Servlet API將數據填充至HTML或其他格式的文檔中。
ModelAndView是Spring MVC中用于封裝控制器方法處理結果的對象,它包含了視圖的邏輯名稱以及模型數據。ModelAndView同時扮演了模型與視圖兩個角色,簡化了控制器返回值的處理,使得控制器能夠在一個對象中同時返回渲染視圖所需的視圖名和模型數據。
ModelAndView類主要包含以下屬性與方法:
在控制器方法中,可以創建并返回一個ModelAndView對象,如下所示:
Java1import org.springframework.stereotype.Controller;
2import org.springframework.web.bind.annotation.GetMapping;
3import org.springframework.web.servlet.ModelAndView;
4
5@Controller
6public class ExampleController {
7
8 @GetMapping("/example")
9 public ModelAndView handleExampleRequest() {
10 ModelAndView modelAndView=new ModelAndView("exampleView");
11 modelAndView.addObject("message", "Hello, World!");
12 return modelAndView;
13 }
14}
在這個例子中,控制器方法創建了一個ModelAndView實例,設置了邏輯視圖名為exampleView,并向模型中添加了一個名為message、值為Hello, World!的屬性。當DispatcherServlet收到此返回值時,它會根據ViewResolver鏈來解析邏輯視圖名,找到對應的視圖實現,并將模型數據傳遞給視圖進行渲染。最終,渲染后的結果作為HTTP響應發送給客戶端。
ModelAndView不僅支持傳統的視圖渲染,還適用于RESTful場景。在返回JSON、XML等非視圖內容時,可以僅設置模型數據,不指定視圖名,Spring MVC會默認選擇合適的HttpMessageConverter將模型數據序列化為響應體。
View與ModelAndView在Spring MVC中分別扮演了視圖層抽象與控制器返回結果封裝的角色。View接口定義了視圖渲染的基本規范,具體的視圖技術通過實現這一接口與Spring MVC框架集成。ModelAndView則作為控制器方法返回值,集中管理視圖名與模型數據,簡化了視圖渲染的流程,確保了模型數據能夠準確、高效地傳遞給視圖進行展現。這兩個組件共同構成了Spring MVC視圖渲染機制的核心,實現了MVC模式中視圖與模型的解耦與協作,為構建松耦合、易維護的Web應用程序提供了強有力的支持。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。