Spring系列之Spring MVC知識

MVC概述

MVC模式就是架構模式的一種,Model(模型)、View(視圖)和Controller(控制)組成,MVC結構可以分成三層:

  1. 最上面的一層,是直接面向最終用戶的視圖層View。它是提供給用戶的操作界面,是程序的外殼
  2. 最底下的一層,是核心的數據層Model,也就是程序需要操作的數據或信息
  3. 中間的一層,是控制層Controller,它負責根據用戶從視圖層輸入的指令,選取數據層中的數據,然後對其進行相應的操作,產生最終結果

這三層是緊密聯繫在一起的,但又是互相獨立的,每一層內部的變化不影響其他層。每一層都對外提供接口,供上面一層調用,實現模塊化。

MVC和MVP

MVP,Model負責數據處理;View負責呈現界面;Presenter負責業務功能,將Model層的數據安全送往View層。Model和View是不能互通的,需要經過中間的Presenter進行傳遞,這一點不同於MVC模式。MVC模式中View可以直接獲取並操作數據,造成View層對數據層的關聯。
相比於MVC模式,MVP模式具有更加嚴格的分層和清晰的結構,這種分層方式更能保證各個層次儘量少的依賴。從MVP的架構來看,Model層邏輯具有高獨立性,但Model層需要依賴數據、網絡、設備等外部因素。Presenter層依賴於Model和View,而View層依賴於Presenter層和設備。

技巧

Controller

在開發MVC應用時,第一步便是編寫Controller,方法有4種:

  1. 使用Spring MVC提供的針對特定目的而設計的控制器類(不常用):
  • AbstractUrlViewController
  • MultiActionController
  • ParameterizableViewController
  • ServletForwardingController
  • ServletWrappingController
  • UrlFilenameViewController
  1. 使用註解@Controller配置(代碼略)
  2. 實現Controller接口並重寫handleRequest()方法:
public class DemoController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return new ModelAndView("main");
    }
}

同時需要添加配置:<bean name="/main" class="aa.bb.DemoController"/>

  1. 若要控制受支持的HTTP方法,會話和內容緩存,還可以繼承AbstractController並重寫方法handleRequestInternal:
public class BigController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return new ModelAndView("big");
    }
}

同時配置:

<bean name="/big" class="aa.bb.BigController">
    <property name="supportedMethods" value="POST"/>
</bean>

@RequestMapping

@RequestMapping可用於指定一個方法要處理的多個URL模式:

@RequestMapping({"/hello", "/hi", "/greetings"})

@RequestMapping可以指定RequestMethod,如RequestMethod.GET;此時可用@GetMapping代替,即等價於@RequestMapping(method = {RequestMethod.GET})

RequestParam

ModelAndView和ModelMap

ModelAndView對象有兩個作用:

  1. 設置轉向地址
    ModelAndView view = new ModelAndView("path:ok");
  2. 用於傳遞控制方法處理結果數據到結果頁面,也就是說把需要在結果頁面上展示的數據放到ModelAndView對象中即可,作用類似於request對象的setAttribute方法的作用,用來在一個請求過程中傳遞處理的數據。通過以下方法向頁面傳遞參數:
    addObject(String key,Object value);
    在頁面上可以通過el變量方式$key或者bboss的一系列數據展示標籤獲取並展示ModelAndView中的數據。

Model 是一個接口, 其實現類爲ExtendedModelMap,繼承ModelMap類。
ModelMap對象主要用於傳遞控制方法處理數據到結果頁面,也就是說把結果頁面上需要的數據放到ModelMap對象中即可,作用類似於request對象的setAttribute方法的作用,用來在一個請求過程中傳遞處理的數據。通過以下方法向頁面傳遞參數:
addAttribute(String key,Object value);
在頁面上可以通過el變量方式$key或者bboss的一系列數據展示標籤獲取並展示modelmap中的數據。

ModelMap本身不能設置頁面跳轉的url地址別名或者物理跳轉地址,可以通過控制器方法的返回值來設置跳轉url地址別名或者物理跳轉地址。
ModelMap的實例是由Spring mvc框架自動創建並作爲控制器方法參數傳入,用戶無需自己創建。

兩者區別

  1. ModelAndView可以設置轉向地址
  2. ModelAndView的實例是由用戶手動創建的

Spring的控制器Controller會返回一個ModelAndView的實例,包括View和Model信息,視圖是以名字爲標識的,ViewResolver是通過名字來解析view的。

如果方法添加註解@ResponseBody ,則會直接將返回值輸出到頁面。

ViewResolver,視圖解析器,提供從視圖名稱到實際視圖的映射。

文件上傳

@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
public String fileUpload(@RequestParam CommonsMultipartFile[] fileUpload) throws Exception {
    for (CommonsMultipartFile item : fileUpload){
        // 存儲上傳的文件
        item.transferTo(new File(item.getOriginalFilename()));
    }
    return "Success";
}

通過自動將上傳數據綁定到CommonsMultipartFile對象數組,默認使用Apache Commons FileUpload作爲文件解析器。

文件下載

HttpServletRequest和HttpServletResponse

示例代碼:

@RequestMapping("/download")
public String doDownloadFile(HttpServletRequest request, HttpServletResponse response) {
    // 訪問請求/響應
    return "";
}

Spring檢測並自動將HttpServletRequest和HttpServletResponse對象注入方法中。

WebFlux & Spring MVC

WebFlux不是Spring MVC的替代方案,WebFlux 也可以運行在Servlet容器上(Servlet 3.1+),WebFlux主要還是應用在異步非阻塞編程模型,而 Spring MVC 是同步阻塞的。兩者可以混合使用。

進階

web.xml中listener、filter和servlet的初始化順序

順序:listener -> filter -> servlet
對使用<listener>標籤聲明的監聽器類進行實例化,調用監聽器類實例對象的contextInitialized()方法,初始化應用上下文數據;其次對使用<filter>標籤聲明的過濾器類進行實例化,調用過濾器類實例對象的init()方法;如果<servlet>標籤內使用<load-on-startup>標籤,則按照數值從小到大的順序對Servlet進行實例化,並調用對應的init()方法。

applicationContext.xml如何自動加載

當運行一個Web項目時,應用服務器(如Tomcat)首先會讀取項目源碼路徑中的web.xml文件,解析其中的配置,發現配置ContextLoaderListener,因此會執行ContextLoaderListener類中的contextInitialized方法,在這個方法中會調用initWebApplicationContext()方法,用於初始化一個WebApplicationContext,即初始化一個Web應用下的Spring容器。在initWebApplicationContext()方法後續代碼實現的內部會根據web.xml中配置的contextConfigLocation屬性加載指定的applicationContext.xml文件,根據這個文件初始化Spring容器。若沒配置contextConfigLocation參數,那麼應用啓動時會默認查找應用根目錄下/WEB-INF/applicationContext.xml文件,也就是說這是一個默認加載的文件路徑

DispatcherServlet

SpringMVC的核心分發器,實現請求分發,是處理請求的入口。DispatcherServlet是一個Servlet,在應用啓動時,DispatcherServlet初始化會執行init方法,DispatcherServlet的init方法繼承自HttpServletBean,在這個初始化方法中會實例化一個WebApplicationContext對象,並且將初始化後的context存到ServletContext中,讓Servlet和Spring容器進行關聯。在DispatcherServlet的onRefresh方法中,初始化各種請求處理策略,如文件上傳處理策略、URL請求處理策略、視圖映射處理策略、異常處理策略等,這些策略的大部分執行邏輯都是先從WebApplicationContext中查找,找不到的情況下再加載和DispatcherServlet同目錄下的DispatcherServlet.properties中的各個策略,例如初始化HandlerMapping,註冊各種請求的處理策略及處理類。

SpringMVC框架在啓動的時候會遍歷Spring容器中的所有bean,對標註@Controller或@RequestMapping註解的類中方法進行遍歷,將類和方法上的@RequestMapping註解值進行合併,使用@RequestMapping註解的相關參數值(如value、method等)封裝一個RequestMappingInfo,將這個Controller實例、方法及方法參數信息(類型、註解等)封裝到HandlerMethod中,然後以RequestMappingInfo爲key,HandlerMethod爲value存到一個以Map爲結構的handlerMethods中。

接着將@RequestMapping註解中的value(即請求路徑)值取出,即url,然後以url爲key,以RequestMappingInfo爲value,存到一個以Map爲結構的urlMap屬性中。

客戶端發起請求時,根據請求的URL到urlMap中查找,找到RequestMappingInfo,然後根據RequestMappingInfo到handlerMethods中查找,找到對應的HandlerMethod,接着將HandlerMethod封裝到HandlerExecutionChain;接着遍歷容器中所有HandlerAdapter實現類,找到支持這次請求的HandlerAdapter,如RequestMappingHandlerAdapter,然後執行SpringMVC攔截器的前置方法(preHandle方法),然後對請求參數解析及轉換,然後(使用反射)調用具體Controller的對應方法返回一個ModelAndView對象,執行攔截器的後置方法(postHandle方法),然後對返回的結果進行處理,最後執行afterCompletion方法。

參考

Spring系列之WebFlux
WebFlux和Spring MVC比較

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章