日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

SpringMVC深度探险(二) —— SpringMVC概览

發(fā)布時間:2025/3/14 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringMVC深度探险(二) —— SpringMVC概览 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文是專欄文章(SpringMVC深度探險)系列的文章之一,博客地址為:http://downpour.iteye.com/blog/1330596

對于任何事物的研究,總是由表及里、由淺入深地進(jìn)行。在本系列的第二篇文章中,我們將通過不同的觀察視角,對SpringMVC做一些概要性的分析,幫助大家了解SpringMVC的基本構(gòu)成要素、SpringMVC的發(fā)展歷程以及SpringMVC的設(shè)計原則。

SpringMVC的構(gòu)成要素

了解一個框架的首要任務(wù)就是搞清楚這個框架的基本構(gòu)成要素。當(dāng)然,這里所說的構(gòu)成要素實際上還可以被挖掘為兩個不同的層次:

  • 基于框架所編寫的應(yīng)用程序的構(gòu)成要素
  • 框架自身的運行主線以及微觀構(gòu)成要素

我們在這里首先來關(guān)注一下第一個層次,因為第一個層次是廣大程序員直接能夠接觸得到的部分。而第二個層次的討論,我們不得不在第一個層次的討論基礎(chǔ)之上通過不斷分析和邏輯論證慢慢給出答案。

在上一篇文章中,我們曾經(jīng)列舉了一段SpringMVC的代碼示例,用于說明MVC框架的構(gòu)成結(jié)構(gòu)。我們在這里不妨將這個示例細(xì)化,總結(jié)歸納出構(gòu)成SpringMVC應(yīng)用程序的基本要素。

1. 指定SpringMVC的入口程序(在web.xml中)

Xml代碼 ?

  • <!--?Processes?application?requests?-->??
  • <servlet>??
  • ????<servlet-name>dispatcher</servlet-name>??
  • ????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>??
  • ????<load-on-startup>1</load-on-startup>??
  • </servlet>??
  • ??????????
  • <servlet-mapping>??
  • ????<servlet-name>dispatcher</servlet-name>??
  • ????<url-pattern>/**</url-pattern>??
  • </servlet-mapping>??
  • <!-- Processes application requests -->

    <servlet>

    ????<servlet-name>dispatcher</servlet-name>

    ????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    ????<load-on-startup>1</load-on-startup>

    </servlet>

    ????????

    <servlet-mapping>

    ????<servlet-name>dispatcher</servlet-name>

    ????<url-pattern>/**</url-pattern>

    </servlet-mapping>



    以一個Servlet作為入口程序是絕大多數(shù)MVC框架都遵循的基本設(shè)計方案。這里的DispatcherServlet被我們稱之為核心分發(fā)器,是SpringMVC最重要的類之一,之后我們會對其單獨展開進(jìn)行分析。

    2. 編寫SpringMVC的核心配置文件(在[servlet-name]-servlet.xml中)

    Xml代碼 ?

  • <beans?xmlns="http://www.springframework.org/schema/beans"??
  • ???????xmlns:mvc="http://www.springframework.org/schema/mvc"??
  • ???????xmlns:context="http://www.springframework.org/schema/context"??
  • ???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
  • ???????xsi:schemaLocation="??
  • ????????????http://www.springframework.org/schema/beans??
  • ????????????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd??
  • ????????????http://www.springframework.org/schema/context???
  • ????????????http://www.springframework.org/schema/context/spring-context-3.1.xsd??
  • ????????????http://www.springframework.org/schema/mvc??
  • ????????????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"???
  • ???????default-autowire="byName">??
  • ??????
  • ????<!--?Enables?the?Spring?MVC?@Controller?programming?model?-->??
  • ????<mvc:annotation-driven?/>??
  • ??????
  • ????<context:component-scan?base-package="com.demo2do"?/>??
  • ??????
  • ??????
  • ????<bean?class="org.springframework.web.servlet.view.InternalResourceViewResolver">??
  • ????????<property?name="prefix"?value="/"?/>??
  • ????????<property?name="suffix"?value=".jsp"?/>??
  • ????</bean>??
  • ??????
  • </beans>??
  • <beans xmlns="http://www.springframework.org/schema/beans"

    ???? xmlns:mvc="http://www.springframework.org/schema/mvc"

    ???? xmlns:context="http://www.springframework.org/schema/context"

    ???? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    ???? xsi:schemaLocation="

    ????????????http://www.springframework.org/schema/beans

    ????????????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

    ????????????http://www.springframework.org/schema/context

    ????????????http://www.springframework.org/schema/context/spring-context-3.1.xsd

    ????????????http://www.springframework.org/schema/mvc

    ????????????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"

    ???? default-autowire="byName">

    ????

    ????<!-- Enables the Spring MVC @Controller programming model -->

    ????<mvc:annotation-driven />

    ????

    ????<context:component-scan base-package="com.demo2do" />

    ???? ?

    ????

    ????<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

    ????????<property name="prefix" value="/" />

    ????????<property name="suffix" value=".jsp" />

    ????</bean>

    ????

    </beans>



    SpringMVC自身由眾多不同的組件共同構(gòu)成,而每一個組件又有眾多不同的實現(xiàn)模式。這里的SpringMVC核心配置文件是定義SpringMVC行為方式的一個窗口,用于指定每一個組件的實現(xiàn)模式。有關(guān)SpringMVC組件的概念,在之后的討論中也會涉及。

    3. 編寫控制(Controller)層的代碼

    Java代碼 ?

  • @Controller??
  • @RequestMapping??
  • public?class?UserController?{??
  • ??
  • ????@RequestMapping("/login")??
  • ????public?ModelAndView?login(String?name,?String?password)?{??
  • ???????//?write?your?logic?here???
  • ???????????return?new?ModelAndView("success");??
  • ????}??
  • ??
  • }??
  • @Controller

    @RequestMapping

    public class UserController {

    ?

    ????@RequestMapping("/login")

    ????public ModelAndView login(String name, String password) {

    ???? // write your logic here????

    return new ModelAndView("success");

    ????}

    ?

    }



    控制(Controller)層的代碼編寫在一個Java文件中。我們可以看到這個Java文件是一個普通的Java類并不依賴于任何接口。只是在響應(yīng)類和響應(yīng)方法上使用了Annotation的語法將它與Http請求對應(yīng)起來。

    從這個例子中,我們實際上已經(jīng)歸納了構(gòu)成基于SpringMVC應(yīng)用程序的最基本要素。它們分別是:

    • 入口程序 —— DispatcherServlet
    • 核心配置 —— [servlet-name]-servlet.xml
    • 控制邏輯 —— UserController

    從應(yīng)用程序自身的角度來看,入口程序和核心配置一旦確定之后將保持固定不變的,而控制邏輯則隨著整個應(yīng)用程序功能模塊的擴(kuò)展而不斷增加。所以在這種編程模式下,應(yīng)用程序的縱向擴(kuò)展非常簡單并且顯得游刃有余。

    基于SpringMVC的應(yīng)用程序能夠表現(xiàn)為現(xiàn)在這個樣子,經(jīng)歷了一個不斷重構(gòu)不斷改造的過程。接下來的討論,我們就來試圖為大家揭秘這個過程。

    SpringMVC的發(fā)展歷程

    在上一篇文章中,我們曾經(jīng)討論過MVC的發(fā)展軌跡。當(dāng)時我們總結(jié)了一個MVC框架的發(fā)展軌跡圖:



    從圖中我們可以發(fā)現(xiàn),所有的MVC框架都是從基本的Servlet模型發(fā)展而來。因此,要了解SpringMVC的發(fā)展歷程,我們還是從最基本的Servlet模型開始,探究SpringMVC對于Servlet模型的改造過程中究竟經(jīng)歷了哪些階段、碰到了哪些問題、并看看SpringMVC是如何解決這些問題的。

    【核心Servlet的提煉】

    在Servlet模型中,請求-響應(yīng)的實現(xiàn)依賴于兩大元素的共同配合:

    1. 配置Servlet及其映射關(guān)系(在web.xml中)

    Xml代碼 ?

  • <servlet>??
  • ????<servlet-name>registerServlet</servlet-name>??
  • ????<servlet-class>com.demo2do.springmvc.web.RegisterServlet</servlet-class>??
  • ????<load-on-startup>1</load-on-startup>??
  • </servlet>??
  • ??????????
  • <servlet-mapping>??
  • ????<servlet-name>registerServlet</servlet-name>??
  • ????<url-pattern>/register</url-pattern>??
  • </servlet-mapping>??
  • <servlet>

    ????<servlet-name>registerServlet</servlet-name>

    ????<servlet-class>com.demo2do.springmvc.web.RegisterServlet</servlet-class>

    ????<load-on-startup>1</load-on-startup>

    </servlet>

    ????????

    <servlet-mapping>

    ????<servlet-name>registerServlet</servlet-name>

    ????<url-pattern>/register</url-pattern>

    </servlet-mapping>



    在這里,<url-pattern>定義了整個請求-響應(yīng)的映射載體:URL;而<servlet-name>則將<servlet>節(jié)點和<servlet-mapping>節(jié)點聯(lián)系在一起形成請求-響應(yīng)的映射關(guān)系;<servlet-class>則定義了具體進(jìn)行響應(yīng)的Servlet實現(xiàn)類。

    2. 在Servlet實現(xiàn)類中完成響應(yīng)邏輯

    Java代碼 ?

  • public?class?RegisterServlet?extends??HttpServlet?{??
  • ??
  • ?????@Override??
  • ?????protected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{??
  • ??????
  • ?????????//?從request獲取參數(shù)??
  • ?????????String?name?=?req.getParameter("name");??
  • ?????????String?birthdayString?=?req.getParameter("birthday");??
  • ???????????
  • ?????????//?做必要的類型轉(zhuǎn)化??
  • ?????????Date?birthday?=?null;??
  • ?????????try?{??
  • ?????????????birthday?=?new?SmpleDateFormat("yyyy-MM-dd").parse(birthdayString);??
  • ?????????}?catch?(ParseException?e)?{??
  • ?????????e.printStackTrace();??
  • ?????????}??
  • ??
  • ?????????//?初始化User類,并設(shè)置字段到user對象中去??
  • ?????????User?user?=?new?User();??
  • ?????????user.setName(name);??
  • ?????????user.setBirthday(birthday);??
  • ??
  • ?????????//?調(diào)用業(yè)務(wù)邏輯代碼完成注冊??
  • ?????????UserService?userService?=?new?UserService();??
  • ?????????userService.register(user);??
  • ???????????
  • ?????????//?設(shè)置返回數(shù)據(jù)??
  • ?????????request.setAttribute("user",?user);??
  • ??
  • ?????????//?返回成功頁面??
  • ?????????req.getRequestDispatcher("/success.jsp").forward(req,?resp);??
  • ?????}??
  • }??
  • public class RegisterServlet extends HttpServlet {

    ?

    @Override

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    ???? ?

    // 從request獲取參數(shù)

    String name = req.getParameter("name");

    String birthdayString = req.getParameter("birthday");

    ?

    // 做必要的類型轉(zhuǎn)化

    Date birthday = null;

    try {

    birthday = new SmpleDateFormat("yyyy-MM-dd").parse(birthdayString);

    } catch (ParseException e) {

    ???? e.printStackTrace();

    }

    ?

    // 初始化User類,并設(shè)置字段到user對象中去

    User user = new User();

    user.setName(name);

    user.setBirthday(birthday);

    ?

    // 調(diào)用業(yè)務(wù)邏輯代碼完成注冊

    UserService userService = new UserService();

    userService.register(user);

    ?

    // 設(shè)置返回數(shù)據(jù)

    request.setAttribute("user", user);

    ?

    // 返回成功頁面

    req.getRequestDispatcher("/success.jsp").forward(req, resp);

    }

    }



    Servlet實現(xiàn)類本質(zhì)上是一個Java類。通過Servlet接口定義中的HttpServletRequest對象,我們可以處理整個請求生命周期中的數(shù)據(jù);通過HttpServletResponse對象,我們可以處理Http響應(yīng)行為。

    整個過程并不復(fù)雜,因為作為一個底層規(guī)范,所規(guī)定的編程元素和實現(xiàn)方式應(yīng)該盡可能直觀和簡單。在這一點上,Servlet規(guī)范似乎可以滿足我們的要求。如果將上述過程中的主要過程加以抽象,我們可以發(fā)現(xiàn)有兩個非常重要概念蘊含在了Servlet的規(guī)范之中:



    控制流和數(shù)據(jù)流的問題幾乎貫穿了所有MVC框架的始末,因而我們不得不在這里率先提出來,希望對讀者有一些警示作用。

    :對于控制流和數(shù)據(jù)流的相關(guān)概念,請參考另外一篇博客:《Struts2技術(shù)內(nèi)幕》 新書部分篇章連載(五)—— 請求響應(yīng)哲學(xué)。這一對概念,幾乎是所有MVC框架背后最為重要的支撐,讀者應(yīng)該尤其重視!

    所有MVC框架的核心問題也由控制流和數(shù)據(jù)流這兩大體系延伸開來。比如,在Servlet編程模型之下,"請求-響應(yīng)映射關(guān)系的定義"這一問題就會隨著項目規(guī)模的擴(kuò)大而顯得力不從心:

    問題1 寫道

    項目規(guī)模擴(kuò)大之后,請求-響應(yīng)的映射關(guān)系全部定義在web.xml中,將造成web.xml的不斷膨脹而變得難以維護(hù)。



    針對這個問題,SpringMVC提出的方案就是:提煉一個核心的Servlet覆蓋對所有Http請求的處理。

    這一被提煉出來的Servlet,通常被我們稱之為:核心分發(fā)器。在SpringMVC中,核心分發(fā)器就是org.springframework.web.servlet.DispatcherServlet

    :核心分發(fā)器的概念并非SpringMVC獨創(chuàng)。我們可以看到,核心分發(fā)器的提煉幾乎是所有MVC框架設(shè)計中的必經(jīng)之路。在Struts2中,也有核心分發(fā)器(Dispatcher)的概念,只是它并不以Servlet的形式出現(xiàn)。因此,讀者應(yīng)該把關(guān)注點放在核心分發(fā)器這個概念的提煉之上,而不是糾結(jié)于其形式。

    有了DispatcherServlet,我們至少從表面上解決了上面的問題。至少在web.xml中,我們的配置代碼就被固定了下來:

    Xml代碼 ?

  • <!--?Processes?application?requests?-->??
  • <servlet>??
  • ????<servlet-name>dispatcherServlet</servlet-name>??
  • ????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>??
  • ????<load-on-startup>1</load-on-startup>??
  • </servlet>??
  • ??????????
  • <servlet-mapping>??
  • ????<servlet-name>dispatcherServlet</servlet-name>??
  • ????<url-pattern>/**</url-pattern>??
  • </servlet-mapping>??
  • <!-- Processes application requests -->

    <servlet>

    ????<servlet-name>dispatcherServlet</servlet-name>

    ????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    ????<load-on-startup>1</load-on-startup>

    </servlet>

    ????????

    <servlet-mapping>

    ????<servlet-name>dispatcherServlet</servlet-name>

    ????<url-pattern>/**</url-pattern>

    </servlet-mapping>



    有了DispatcherServlet,我們只相當(dāng)于邁出了堅實的第一步,因為對核心Servlet的提煉不僅僅是將所有的Servlet集中在一起那么簡單,我們還將面臨兩大問題:

    問題2 寫道

    核心Servlet應(yīng)該能夠根據(jù)一定的規(guī)則對不同的Http請求分發(fā)到不同的Servlet對象上去進(jìn)行處理。

    ?

    問題3 寫道

    核心Servlet應(yīng)該能夠建立起一整套完整的對所有Http請求進(jìn)行規(guī)范化處理的流程。



    而這兩大問題的解決,涉及到了DispatcherServlet的設(shè)計核心。我們也不得不引入另外一個重要的編程元素,那就是:組件

    【組件的引入】

    DispatcherServlet的引入是我們通過加入新的編程元素來對基本的Servlet規(guī)范進(jìn)行抽象概括所邁出的第一步。不過接下來,有關(guān)DispatcherServlet的設(shè)計問題又一次擺到了我們的面前。

    如果仔細(xì)分析一下上一節(jié)末尾所提出的兩個問題,我們可以發(fā)現(xiàn)這兩個問題實際上都涉及到了DispatcherServlet的處理過程,這一處理過程首先必須是一劑萬能藥,能夠處理所有的Http請求;同時,DispatcherServlet還需要完成不同協(xié)議之間的轉(zhuǎn)化工作(從Http協(xié)議到Java世界的轉(zhuǎn)化)。

    對此,SpringMVC所提出的方案是:將整個處理流程規(guī)范化,并把每一個處理步驟分派到不同的組件中進(jìn)行處理

    這個方案實際上涉及到兩個方面:

    • 處理流程規(guī)范化 —— 將處理流程劃分為若干個步驟(任務(wù)),并使用一條明確的邏輯主線將所有的步驟串聯(lián)起來
    • 處理流程組件化 —— 將處理流程中的每一個步驟(任務(wù))都定義為接口,并為每個接口賦予不同的實現(xiàn)模式

    在SpringMVC的設(shè)計中,這兩個方面的內(nèi)容總是在一個不斷交叉、互為補充的過程中逐步完善的。

    處理流程規(guī)范化是目的,對于處理過程的步驟劃分和流程定義則是手段。因而處理流程規(guī)范化的首要內(nèi)容就是考慮一個通用的Servlet響應(yīng)程序大致應(yīng)該包含的邏輯步驟:

    • 步驟1 —— 對Http請求進(jìn)行初步處理,查找與之對應(yīng)的Controller處理類(方法)
    • 步驟2 —— 調(diào)用相應(yīng)的Controller處理類(方法)完成業(yè)務(wù)邏輯
    • 步驟3 —— 對Controller處理類(方法)調(diào)用時可能發(fā)生的異常進(jìn)行處理
    • 步驟4 —— 根據(jù)Controller處理類(方法)的調(diào)用結(jié)果,進(jìn)行Http響應(yīng)處理

    這些邏輯步驟雖然還在我們的腦海中,不過這些過程恰恰正是我們對整個處理過程的流程化概括,稍后我們就會把它們進(jìn)行程序化處理。

    所謂的程序化,實際上也就是使用編程語言將這些邏輯語義表達(dá)出來。在Java語言中,最適合表達(dá)邏輯處理語義的語法結(jié)構(gòu)是接口,因此上述的四個流程也就被定義為了四個不同接口,它們分別是:

    • 步驟1 —— HandlerMapping
    • 步驟2 —— HandlerAdapter
    • 步驟3 —— HandlerExceptionResolver
    • 步驟4 —— ViewResolver

    結(jié)合之前我們對流程組件化的解釋,這些接口的定義不正是處理流程組件化的步驟嘛?這些接口,就是組件

    除了上述組件之外,SpringMVC所定義的組件幾乎涵蓋了每一個處理過程中的重要節(jié)點。我們在這里引用Spring官方reference中對于最基本的組件的一些說明:



    我們在之后篇文章中將重點對這里所提到的所有組件做深入的分析。大家在這里需要理解的是SpringMVC定義這些組件的目的和初衷。

    這些組件一旦被定義,自然而然也就引出了下一個問題:這些組件是如何串聯(lián)在一起的?這個過程,是在DispatcherServlet中完成的。有關(guān)這一點,我們可以從兩個不同的角度加以證明。

    1. 從DispatcherServlet自身數(shù)據(jù)結(jié)構(gòu)的角度



    如圖中所示,DispatcherServlet中包含了眾多SpringMVC的組件,這些組件是實現(xiàn)DispatcherServlet核心邏輯的基礎(chǔ)。

    2. 從DispatcherServlet的核心源碼的角度

    Java代碼 ?

  • try?{??
  • ????//?這里省略了部分代碼??
  • ??
  • ????//?獲取HandlerMapping組件返回的執(zhí)行鏈??
  • ????mappedHandler?=?getHandler(processedRequest,?false);??
  • ????if?(mappedHandler?==?null?||?mappedHandler.getHandler()?==?null)?{??
  • ????????noHandlerFound(processedRequest,?response);??
  • ????????return;??
  • ????}??
  • ??
  • ????//?獲取HandlerAdapter組件??
  • ????HandlerAdapter?ha?=?getHandlerAdapter(mappedHandler.getHandler());??
  • ??
  • ????//?這里省略了部分源碼??
  • ??????
  • ????//?調(diào)用HandlerAdapter組件??
  • ????mv?=?ha.handle(processedRequest,?response,?mappedHandler.getHandler());??
  • ??
  • ????//?這里省略了部分源碼??
  • ??
  • }catch?(ModelAndViewDefiningException?ex)?{??
  • ????logger.debug("ModelAndViewDefiningException?encountered",?ex);??
  • ????mv?=?ex.getModelAndView();??
  • }catch?(Exception?ex)?{??
  • ????Object?handler?=?(mappedHandler?!=?null???mappedHandler.getHandler()?:?null);??
  • ????//?調(diào)用HandlerExceptionResolver進(jìn)行異常處理??
  • ????mv?=?processHandlerException(processedRequest,?response,?handler,?ex);??
  • ????errorView?=?(mv?!=?null);??
  • }??
  • try {

    ????// 這里省略了部分代碼

    ?

    ????// 獲取HandlerMapping組件返回的執(zhí)行鏈

    ????mappedHandler = getHandler(processedRequest, false);

    ????if (mappedHandler == null || mappedHandler.getHandler() == null) {

    ????????noHandlerFound(processedRequest, response);

    ????????return;

    ????}

    ?

    ????// 獲取HandlerAdapter組件

    ????HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    ?

    ????// 這里省略了部分源碼

    ????

    ????// 調(diào)用HandlerAdapter組件

    ????mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    ?

    ????// 這里省略了部分源碼

    ?

    }catch (ModelAndViewDefiningException ex) {

    ????logger.debug("ModelAndViewDefiningException encountered", ex);

    ????mv = ex.getModelAndView();

    }catch (Exception ex) {

    ????Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);

    ????// 調(diào)用HandlerExceptionResolver進(jìn)行異常處理

    ????mv = processHandlerException(processedRequest, response, handler, ex);

    ????errorView = (mv != null);

    }



    從上面的代碼片段中,我們可以看到DispatcherServlet的核心邏輯不過是對組件的獲取和調(diào)用。

    除此之外,SpringMVC對處理流程的規(guī)范化和組件化所引出的另外一個問題就是如何針對所有的組件進(jìn)行管理

    先說說管理。其實管理這些組件對于SpringMVC來說完全不是問題,因為SpringMVC作為Spring Framework的一部分,其自身的運行環(huán)境就是Spring所定義的容器之中。我們知道,Spring Framework的核心作用之一就是對整個應(yīng)用程序的組件進(jìn)行管理。所以SpringMVC對于這些已定義組件的管理,只不過是借用了Spring自身已經(jīng)提供的容器功能而已。

    :SpringMVC在進(jìn)行組件管理時,會單獨為SpringMVC相關(guān)的組件構(gòu)建一個容器環(huán)境,這一容器環(huán)境可以獨立于應(yīng)用程序自身所創(chuàng)建的Spring容器。有關(guān)這一點,我們在之后的討論中將詳細(xì)給出分析。

    而SpringMVC對這些組件的管理載體,就是我們在上一節(jié)中所提到的核心配置文件。我們可以看到,核心配置文件在整個SpringMVC的構(gòu)成要素中占有一席之地的重要原因就是在于:我們必須借助一個有效的手段對整個SpringMVC的組件進(jìn)行定義,而這一點正是通過核心配置文件來完成的。

    如果我們把上面的整個過程重新梳理一下,整個邏輯看起來就像這樣:



    這四個方面的內(nèi)容,我們是順著設(shè)計思路的不斷推進(jìn)而總結(jié)歸納出來的。這也恰好證明之前所提到的一個重要觀點,我們在這里強調(diào)一下:

    downpour 寫道

    處理流程的規(guī)范化和組件化,是在一個不斷交叉、互為補充的過程中逐步完善的。



    【行為模式的擴(kuò)展】

    有了組件,也有了DispatcherServlet對所有組件的串聯(lián),我們之前所提出的兩個問題似乎已經(jīng)可以迎刃而解。所以,我們可以說:

    downpour 寫道

    SpringMVC就是通過DispatcherServlet將一堆組件串聯(lián)起來的Web框架。



    在引入組件這個概念的時候,我們所強調(diào)的是處理流程的抽象化,因而所有組件的外在表現(xiàn)形式是接口。接口最重要意義是定義操作規(guī)范,所以接口用來表達(dá)每一個處理單元的邏輯語義是最合適不過的。但光有接口,并不能完整地構(gòu)成一個框架的行為模式。從操作規(guī)范到行為模式的變化,是由接口所對應(yīng)的實現(xiàn)類來完成的。

    在Java語言中,一個接口可以有多個不同的實現(xiàn)類,從而構(gòu)成一個樹形的實現(xiàn)體系。而每一個不同的實現(xiàn)分支,實際上代表的是對于相同的邏輯語義的不同解讀方式。結(jié)合上面我們的描述,也可以說:一個接口的每一個不同的實現(xiàn)分支,代表了相同操作規(guī)范的不同行為模式。

    我們可以通過之前曾經(jīng)提到過的一個SpringMVC組件HandlerMapping為例進(jìn)行說明。



    上圖就是HandlerMapping接口的樹形實現(xiàn)體系。在這個實現(xiàn)體系結(jié)構(gòu)中,每一個樹形結(jié)構(gòu)的末端實現(xiàn)都是SpringMVC中比較具有典型意義的行為模式。我們可以截取其中的幾個實現(xiàn)來加以說明:

    • BeanNameUrlHandlerMapping —— 根據(jù)Spring容器中的bean的定義來指定請求映射關(guān)系
    • SimpleUrlHandlerMapping —— 直接指定URL與Controller的映射關(guān)系,其中的URL支持Ant風(fēng)格
    • DefaultAnnotationHandlerMapping —— 支持通過直接掃描Controller類中的Annotation來確定請求映射關(guān)系
    • RequestMappingHandlerMapping —— 通過掃描Controller類中的Annotation來確定請求映射關(guān)系的另外一個實現(xiàn)類

    有關(guān)這幾個實現(xiàn)類的具體示例和使用說明,讀者可以參考不同版本的Spring官方文檔來獲取具體的細(xì)節(jié)。

    :我們在這里之所以要強調(diào)不同版本的Spring官方文檔的原因在于這些不同的實現(xiàn)類,正代表了不同版本SpringMVC在默認(rèn)行為模式上選擇的不同。在下圖中,我們列出了不同重大版本的SpringMVC的實現(xiàn)體系結(jié)構(gòu),并用紅色框圈出了每個版本默認(rèn)的實現(xiàn)類。



    我們可以看到,上述這些不同的HandlerMapping的實現(xiàn)類,其運行機(jī)制和行為模式完全不同。這也就意味著對于HandlerMapping這個組件而言,可以進(jìn)行選擇的余地就很大。我們既可以選擇其中的一種實現(xiàn)模式作為默認(rèn)的行為模式,也可以將這些實現(xiàn)類依次串聯(lián)起來成為一個執(zhí)行鏈。不過這已經(jīng)是實現(xiàn)層面和設(shè)計模式上的小技巧了。

    單就HandlerMapping一個組件,我們就能看到各種不同的行為模式。如果我們將邏輯主線中所有的組件全部考慮進(jìn)來,那么整個實現(xiàn)機(jī)制就會隨著這些組件實現(xiàn)體系的不同而表現(xiàn)出截然不同的行為方式了。因此,我們的結(jié)論是:

    downpour 寫道

    SpringMVC各種不同的組件實現(xiàn)體系成為了SpringMVC行為模式擴(kuò)展的有效途徑。



    有關(guān)SpringMVC的各種組件和實現(xiàn)體系,我們將在之后的討論中詳細(xì)展開。

    SpringMVC的設(shè)計原則

    最后我們來討論一下SpringMVC的設(shè)計原則。任何框架在設(shè)計的時候都必須遵循一些基本的原則,而這些原則也成為整個框架的理論基礎(chǔ)。對于那些有一定SpringMVC使用經(jīng)驗的程序員來說,這些基本的設(shè)計原則本身也一定是給大家留下深刻印象的那些閃光點,所以我們非常有必要在這里加以總結(jié)。

    【Open for extension / closed for modification】

    這條重要的設(shè)計原則被寫在了Spring官方的reference中SpringMVC章節(jié)的起始段:

    Spring Reference 寫道

    A key design principle in Spring Web MVC and in Spring in general is the "Open for extension, closed for modification" principle.



    SpringMVC在整個官方reference的起始就強調(diào)這一原則,可見其對于整個框架的重要性。那么我們又如何來理解這段話的含義呢?筆者在這里從源碼的角度歸納了四個方面:

    1. 使用final關(guān)鍵字來限定核心組件中的核心方法

    有關(guān)這一點,我們還可以在Spring官方的reference中找到非常明確的說明:

    Spring Reference 寫道

    Some methods in the core classes of Spring Web MVC are marked final. As a developer you cannot override these methods to supply your own behavior. This has not been done arbitrarily, but specifically with this principle in mind.



    在SpringMVC的源碼中,HandlerAdapter實現(xiàn)類RequestMappingHandlerAdapter中,核心方法handleInternal就被定義為final:

    downpour 寫道

    結(jié)論? As a developer you cannot override these methods to supply your own behavior



    2. 大量地在核心組件中使用private方法

    我們依然以SpringMVC默認(rèn)的HandlerAdapter實現(xiàn)RequestMappingHandlerAdapter為例進(jìn)行說明:




    可以看到,幾乎所有的核心處理方法全部被定義成了帶有紅色標(biāo)記的private方法,這就充分表明了SpringMVC對于"子類擴(kuò)展"這種方式的態(tài)度:

    downpour 寫道

    結(jié)論? 子類不允許通過繼承的方式改變父類的默認(rèn)行為。



    3. 限定某些類對外部程序不可見

    有關(guān)這一點,有好幾個類可以加以證明,我們不妨來看看它們的源碼定義:

    Java代碼 ?

  • class?AnnotationDrivenBeanDefinitionParser?implements?BeanDefinitionParser?{??
  • ????//?這里省略了所有的代碼??
  • }??
  • ??
  • class?DefaultServletHandlerBeanDefinitionParser?implements?BeanDefinitionParser?{??
  • ????//?這里省略了所有的代碼??
  • }??
  • ??
  • class?InterceptorsBeanDefinitionParser?implements?BeanDefinitionParser?{??
  • ????//?這里省略了所有的代碼??
  • }??
  • ??
  • class?ResourcesBeanDefinitionParser?implements?BeanDefinitionParser?{??
  • ????//?這里省略了所有的代碼??
  • }??
  • class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

    // 這里省略了所有的代碼

    }

    ?

    class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {

    // 這里省略了所有的代碼

    }

    ?

    class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {

    // 這里省略了所有的代碼

    }

    ?

    class ResourcesBeanDefinitionParser implements BeanDefinitionParser {

    // 這里省略了所有的代碼

    }

    ?

    downpour 寫道

    結(jié)論? 不允許外部程序?qū)@些系統(tǒng)配置類進(jìn)行訪問,從而杜絕外部程序?qū)pringMVC默認(rèn)行為的任何修改。



    在這些類的定義中,我們并未看到public修飾符。也就是說,這些類只能在SpringMVC的內(nèi)部被調(diào)用,對于框架以外的應(yīng)用程序是不可見的。有關(guān)這些類的作用,我們將在之后的討論中詳細(xì)展開。

    4. 提供自定義擴(kuò)展接口,卻不提供完整覆蓋默認(rèn)行為的方式

    這一點,需要深入到SpringMVC的請求處理內(nèi)部才能夠體會得到,我們在這里截取了其中的一段源碼加以說明:

    Java代碼 ?

  • private?List<HandlerMethodArgumentResolver>?getDefaultArgumentResolvers()?{??
  • ????List<HandlerMethodArgumentResolver>?resolvers?=?new?ArrayList<HandlerMethodArgumentResolver>();??
  • ??
  • ????//?Annotation-based?argument?resolution??
  • ????resolvers.add(new?RequestParamMethodArgumentResolver(getBeanFactory(),?false));??
  • ????resolvers.add(new?RequestParamMapMethodArgumentResolver());??
  • ????resolvers.add(new?PathVariableMethodArgumentResolver());??
  • ????resolvers.add(new?ServletModelAttributeMethodProcessor(false));??
  • ????resolvers.add(new?RequestResponseBodyMethodProcessor(getMessageConverters()));??
  • ????resolvers.add(new?RequestPartMethodArgumentResolver(getMessageConverters()));??
  • ????resolvers.add(new?RequestHeaderMethodArgumentResolver(getBeanFactory()));??
  • ????resolvers.add(new?RequestHeaderMapMethodArgumentResolver());??
  • ????resolvers.add(new?ServletCookieValueMethodArgumentResolver(getBeanFactory()));??
  • ????resolvers.add(new?ExpressionValueMethodArgumentResolver(getBeanFactory()));??
  • ??
  • ????//?Type-based?argument?resolution??
  • ????resolvers.add(new?ServletRequestMethodArgumentResolver());??
  • ????resolvers.add(new?ServletResponseMethodArgumentResolver());??
  • ????resolvers.add(new?HttpEntityMethodProcessor(getMessageConverters()));??
  • ????resolvers.add(new?RedirectAttributesMethodArgumentResolver());??
  • ????resolvers.add(new?ModelMethodProcessor());??
  • ????resolvers.add(new?MapMethodProcessor());??
  • ????resolvers.add(new?ErrorsMethodArgumentResolver());??
  • ????resolvers.add(new?SessionStatusMethodArgumentResolver());??
  • ????resolvers.add(new?UriComponentsBuilderMethodArgumentResolver());??
  • ??
  • ????//?Custom?arguments??
  • ????if?(getCustomArgumentResolvers()?!=?null)?{??
  • ????????resolvers.addAll(getCustomArgumentResolvers());??
  • ????}??
  • ??
  • ????//?Catch-all??
  • ????resolvers.add(new?RequestParamMethodArgumentResolver(getBeanFactory(),?true));??
  • ????resolvers.add(new?ServletModelAttributeMethodProcessor(true));??
  • ??
  • ????return?resolvers;??
  • }??
  • ????private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {

    ????????List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

    ?

    ????????// Annotation-based argument resolution

    ????????resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));

    ????????resolvers.add(new RequestParamMapMethodArgumentResolver());

    ????????resolvers.add(new PathVariableMethodArgumentResolver());

    ????????resolvers.add(new ServletModelAttributeMethodProcessor(false));

    ????????resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));

    ????????resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));

    ????????resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));

    ????????resolvers.add(new RequestHeaderMapMethodArgumentResolver());

    ????????resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));

    ????????resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

    ?

    ????????// Type-based argument resolution

    ????????resolvers.add(new ServletRequestMethodArgumentResolver());

    ????????resolvers.add(new ServletResponseMethodArgumentResolver());

    ????????resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));

    ????????resolvers.add(new RedirectAttributesMethodArgumentResolver());

    ????????resolvers.add(new ModelMethodProcessor());

    ????????resolvers.add(new MapMethodProcessor());

    ????????resolvers.add(new ErrorsMethodArgumentResolver());

    ????????resolvers.add(new SessionStatusMethodArgumentResolver());

    ????????resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    ?

    ????????// Custom arguments

    ????????if (getCustomArgumentResolvers() != null) {

    ????????????resolvers.addAll(getCustomArgumentResolvers());

    ????????}

    ?

    ????????// Catch-all

    ????????resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

    ????????resolvers.add(new ServletModelAttributeMethodProcessor(true));

    ?

    ????????return resolvers;

    ????}



    這是RequestMappingHandlerAdapter內(nèi)部的一個重要方法,用以獲取所有的參數(shù)處理實現(xiàn)類(HandlerMethodArgumentResolver)。從源碼中,我們可以看到雖然這個方法是一個private的方法,但是它在源碼中卻提供了getCustomArgumentResolvers()方法作為切入口,允許用戶自行進(jìn)行擴(kuò)展。不過我們同樣可以發(fā)現(xiàn),用戶自定義的擴(kuò)展類,只是被插入到整個尋址過程中,并不能通過用戶自定義的擴(kuò)展類來實現(xiàn)對其他HandlerMethodArgumentResolver行為的覆蓋;也不能改變HandlerMethodArgumentResolver的處理順序。也就是說:

    downpour 寫道

    結(jié)論? SpringMVC提供的擴(kuò)展切入點無法改變框架默認(rèn)的行為方式。



    上述這四個方面,都是這一條設(shè)計原則在源碼級別的佐證。或許有的讀者會產(chǎn)生這樣的疑慮:這個不能改,那個也不能改,我們對于SpringMVC的使用豈不是喪失了很多靈活性?這個疑慮的確存在,但是只說對了一半。因為SpringMVC的這一條設(shè)計原則說的是:不能動其根本,只能在一定范圍內(nèi)進(jìn)行擴(kuò)展。

    至于說到SpringMVC為什么會基于這樣一條設(shè)計原則,這里面的原因很多。除了之前所提到的編程模型和組件模型的影響,其中更加牽涉到一個編程哲學(xué)的取向問題。有關(guān)這一點,我們在之后的文章中將陸續(xù)展開。

    【形散神不散】

    這一條編程原則實際上與上一條原則只是在表達(dá)方式上有所不同,其表達(dá)的核心意思是比較類似的。那么我們?nèi)绾蝸矶x這里的"形"和"神"呢?

    • —— SpringMVC總是沿著一條固定的邏輯主線運行
    • —— SpringMVC卻擁有多種不同的行為模式

    SpringMVC是一個基于組件的開發(fā)框架,組件的不同實現(xiàn)體系構(gòu)成了"形";組件的邏輯串聯(lián)構(gòu)成了"神"。因此,"形散神不散",實際上是說:

    downpour 寫道

    結(jié)論? SpringMVC的邏輯主線始終不變,而行為模式卻可以多種多樣。



    我們在之前有關(guān)組件的討論中,已經(jīng)見識到了組件的實現(xiàn)體系,也領(lǐng)略了在不同的SpringMVC版本中,組件的行為模式的不同。這些已經(jīng)能夠充分證明"形散"的事實。接下來,我們再通過源碼來證明一下"神不散":



    圖中的代碼是DispatcherServlet中的核心方法doDispatch,我們這里使用了比較工具將Spring3.1中的實現(xiàn)代碼和Spring2.0.8中的實現(xiàn)代碼做了比較,其中的區(qū)別之處比較工具使用了不同的顏色標(biāo)注了出來。

    我們可以很明顯地看到,雖然Spring2.0到Spring3.1之間,SpringMVC的行為方式已經(jīng)有了翻天覆地的變化,然而整個DispatcherServlet的核心處理主線卻并沒有很大的變化。這種穩(wěn)定性,恰巧證明了整個SpringMVC的體系結(jié)構(gòu)設(shè)計的精妙之處。

    【簡化、簡化、還是簡化】

    在Spring2.5之前的SpringMVC版本并沒有很強的生命力,因為它只是通過組件將整個MVC的概念加以詮釋,從開發(fā)流程的簡易度來看并沒有很明顯的提升。有關(guān)SpringMVC發(fā)展的里程碑,我們將在之后篇文章中重點講述。我們在這里想要談到的SpringMVC的另外一大設(shè)計原則,實際上主要是從Spring2.5這個版本之后才不斷顯現(xiàn)出來的。這條設(shè)計原則可以用2個字來概括:簡化

    這里說的簡化,其實包含的內(nèi)容非常廣泛。筆者在這里挑選了兩個比較重要的方面來進(jìn)行說明:

    • Annotation —— 簡化各類配置定義
    • Schema Based XML —— 簡化組件定義

    先談?wù)凙nnotation。Annotation是JDK5.0帶來的一種全新的Java語法。這種語法的設(shè)計初衷眾說紛紜,并沒有一個標(biāo)準(zhǔn)的答案。筆者在這里給出一個個人觀點以供參考:

    downpour 寫道

    結(jié)論? Annotation的原型是注釋。作為一種對注釋的擴(kuò)展而被引入成為一個語法要素,其本身就是為了對所標(biāo)注的編程元素進(jìn)行補充說明,從而進(jìn)一步完善編程元素的邏輯語義。



    從這個結(jié)論中,我們可以看到一層潛在的意思:在Annotation出現(xiàn)之前,Java自身語法所定義的編程元素已經(jīng)不足以表達(dá)足夠多的信息或者邏輯語義。在這種情況下,過去經(jīng)常使用的方法是引入新的編程元素(例如使用最多的就是XML形式的結(jié)構(gòu)化配置文件)來對Java程序進(jìn)行補充說明。而在Annotation出現(xiàn)之后,可以在一定程度上有效解決這一問題。因此Annotation在很長一段時間都被當(dāng)作是XML配置文件的替代品。

    這也就是Annotation經(jīng)常被用來和XML進(jìn)行比較的原因。孰優(yōu)孰劣其實還是要視具體情況而定,并沒有什么標(biāo)準(zhǔn)答案。不過我們在這里想強調(diào)的是Annotation在整個SpringMVC中所起到的作用,并非僅僅是代替XML那么簡單。我們歸納了有三個不同的方面:

    1. 簡化請求映射的定義

    在Spring2.5之前,所有的Http請求與Controller核心處理器之間的映射關(guān)系都是在XML文件中定義的。作為XML配置文件的有效替代品,Annotation接過了定義映射關(guān)系的重任。我們可以將@RequestMapping加在Controller的class-level和method-level進(jìn)行Http請求的抽象。

    2. 消除Controller對接口的依賴

    在Spring2.5之前,SpringMVC規(guī)定所有的Controller都必須實現(xiàn)Controller接口:

    Java代碼 ?

  • public?interface?Controller?{??
  • ??
  • ????/**?
  • ?????*?Process?the?request?and?return?a?ModelAndView?object?which?the?DispatcherServlet?
  • ?????*?will?render.?A?<code>null</code>?return?value?is?not?an?error:?It?indicates?that?
  • ?????*?this?object?completed?request?processing?itself,?thus?there?is?no?ModelAndView?
  • ?????*?to?render.?
  • ?????*?@param?request?current?HTTP?request?
  • ?????*?@param?response?current?HTTP?response?
  • ?????*?@return?a?ModelAndView?to?render,?or?<code>null</code>?if?handled?directly?
  • ?????*?@throws?Exception?in?case?of?errors?
  • ?????*/??
  • ????ModelAndView?handleRequest(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception;??
  • ??
  • }??
  • public interface Controller {

    ?

    ????/**

    ???? * Process the request and return a ModelAndView object which the DispatcherServlet

    ???? * will render. A <code>null</code> return value is not an error: It indicates that

    ???? * this object completed request processing itself, thus there is no ModelAndView

    ???? * to render.

    ???? * @param request current HTTP request

    ???? * @param response current HTTP response

    ???? * @return a ModelAndView to render, or <code>null</code> if handled directly

    ???? * @throws Exception in case of errors

    ???? */

    ????ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

    ?

    }



    也就是說,應(yīng)用程序不得不嚴(yán)重依賴于接口所規(guī)定的處理模式。而我們看到Controller接口除了對處理接口的返回值做了一次封裝以外,我們依然需要面對原生的HttpServletRequest和HttpServletResponse對象進(jìn)行操作。

    而在Spring2.5之后,我們可以通過@Controller來指定SpringMVC可識別的Controller,徹底消除了對接口的依賴:

    Java代碼 ?

  • @Controller??
  • public?class?UserController?{??
  • ??????//?這里省略了許多代碼??
  • }??
  • @Controller

    public class UserController {

    // 這里省略了許多代碼

    }



    3. 成為框架進(jìn)行邏輯處理的標(biāo)識

    之前已經(jīng)談到,Annotation主要被用于對編程元素進(jìn)行補充說明。因而Spring就利用這一特性,使得那些被加入了特殊Annotation的編程元素可以得到特殊的處理。例如,SpringMVC引入的@SessionAttribute、@RequestBody、@ModelAttribute等等,可以說既是對Controller的一種邏輯聲明,也成為了框架本身對相關(guān)元素進(jìn)行處理的一個標(biāo)識符。

    再談?wù)凷chema Based XML。Schema Based XML并不是一個陌生的概念,早在Spring2.0時代就被用于進(jìn)行XML配置的簡化。SpringMVC在進(jìn)入到3.0版本之后,正式將其引入并作為SpringMVC組件定義的一個重要手段。

    在XML中引入Schema,只需要在XML文件的開頭加入相關(guān)的定義。例如:

    Xml代碼 ?

  • <beans?xmlns="http://www.springframework.org/schema/beans"??
  • ???????xmlns:mvc="http://www.springframework.org/schema/mvc"??
  • ???????xmlns:context="http://www.springframework.org/schema/context"??
  • ???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
  • ???????xsi:schemaLocation="??
  • ????????????http://www.springframework.org/schema/beans??
  • ????????????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd??
  • ????????????http://www.springframework.org/schema/context???
  • ????????????http://www.springframework.org/schema/context/spring-context-3.1.xsd??
  • ????????????http://www.springframework.org/schema/mvc??
  • ????????????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">??
  • ??
  • ??
  • </beans>??
  • <beans xmlns="http://www.springframework.org/schema/beans"

    ???? xmlns:mvc="http://www.springframework.org/schema/mvc"

    ???? xmlns:context="http://www.springframework.org/schema/context"

    ???? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    ???? xsi:schemaLocation="

    ????????????http://www.springframework.org/schema/beans

    ????????????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

    ????????????http://www.springframework.org/schema/context

    ????????????http://www.springframework.org/schema/context/spring-context-3.1.xsd

    ????????????http://www.springframework.org/schema/mvc

    ????????????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    ?

    ?

    </beans>



    而Schema的具體處理,則位于Spring的JAR中的/META-INF/spring.handlers文件中進(jìn)行定義:

    Xml代碼 ?

  • http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler??
  • http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler



    我們會在之后的討論中詳細(xì)分析MvcNamespaceHandler的源碼。不過我們可以明確的是,在我們使用Schema Based XML的同時,有許多SpringMVC的內(nèi)置對象會被預(yù)先定義成為組件,我們的配置將是對這些預(yù)先定義好的組件的一個二次配置的過程。可以想象,二次配置一定會比較省力,因為它至少省去了很多內(nèi)置對象的定義過程。這也就是Schema Based XML帶來的簡化效果了。

    小結(jié)

    本文從邏輯上講,可以分成三個部分:

    • SpringMVC的構(gòu)成要素 —— 是什么 —— 闡述框架的主體結(jié)構(gòu)
    • SpringMVC的發(fā)展歷程 —— 為什么 —— 闡述框架各要素產(chǎn)生的內(nèi)因
    • SpringMVC的設(shè)計原則 —— 怎么樣 —— 闡述框架的共性思想

    "是什么"是框架最根本的問題。我們從SpringMVC的三要素入手,幫助大家分析構(gòu)成SpringMVC的基本元素主要是為了讓讀者對整個SpringMVC的架構(gòu)有一個宏觀的認(rèn)識。在之后的分析中,我們研究的主體內(nèi)容也將始終圍繞著這些SpringMVC的構(gòu)成要素,并進(jìn)行逐一分析。

    "為什么"是框架的存在基礎(chǔ)。我們可以看到,整個SpringMVC的發(fā)展歷程是一個對于開發(fā)模式不斷進(jìn)行優(yōu)化的過程,也是不斷解決Web開發(fā)中所面臨的一個又一個問題的過程。之前我們也曾經(jīng)提到過一個重要觀點:任何框架無所謂好與壞、優(yōu)與劣,它們只是在不同的領(lǐng)域解決問題的方式不同。所以,我們分析這些SpringMVC基本構(gòu)成要素產(chǎn)生的原因?qū)嶋H上也是對整個Web開發(fā)進(jìn)行重新思考的過程。

    "怎么樣"是一種深層次的需求。對于SpringMVC而言,了解其基本構(gòu)成和用法并不是一件難事,但是要從中提煉并總結(jié)出一些共性的東西就需要我們能夠站在一個更高的高度來進(jìn)行分析。也只有了解了這些共性的東西,我們才能進(jìn)一步總結(jié)出使用框架的最佳實踐。

    讀到這里,希望讀者能夠回味一下本文的寫作思路,并且能夠舉一反三將這種思考問題的方式運用到其他一些框架的學(xué)習(xí)中去。這樣,本文的目的也就達(dá)到了。

    轉(zhuǎn)載于:https://www.cnblogs.com/xiangxs/p/5051649.html

    總結(jié)

    以上是生活随笔為你收集整理的SpringMVC深度探险(二) —— SpringMVC概览的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。