Spring詳解7.Spring MVC

一年又一年成畦,字節(jié)跳動(dòng) Lark(飛書) 研發(fā)團(tuán)隊(duì)又雙叒叕開始招新生啦!
【內(nèi)推碼】:GTPUVBA
【內(nèi)推鏈接】:https://job.toutiao.com/s/JRupWVj
【招生對(duì)象】:20年9月后~21年8月前 畢業(yè)的同學(xué)
【報(bào)名時(shí)間】:6.16-7.16(提前批簡(jiǎn)歷投遞只有一個(gè)月抓住機(jī)會(huì)哦@钥)
【畫重點(diǎn)】:提前批和正式秋招不矛盾循帐!面試成功,提前鎖定Offer忠寻;若有失利惧浴,額外獲得一次面試機(jī)會(huì)存和,正式秋招開啟后還可再次投遞奕剃。

點(diǎn)擊進(jìn)入我的博客

更多章節(jié)

Spring詳解1.概述
Spring詳解2.理解IoC容器
Spring詳解3.Bean的裝配
Spring詳解4.容器內(nèi)幕
Spring詳解5.AOP
Spring詳解6.基于AspectJ的AOP
Spring詳解7.Spring MVC
Spring詳解8.Spring DAO

1 概述

1.1 Spring特點(diǎn)

  • 注解驅(qū)動(dòng):Spring MVC通過一套MVC注解,讓POJO成為處理請(qǐng)求的控制器捐腿,無需實(shí)現(xiàn)任何接口
  • REST風(fēng)格:Spring MVC支持REST風(fēng)格的URL請(qǐng)求

1.2 核心內(nèi)容

Spring MVC框架圍繞DispatcherServlet這個(gè)核心展開纵朋,它負(fù)責(zé)截獲請(qǐng)求并將其分派給相應(yīng)的處理器處理。Spring MVC框架包括注解驅(qū)動(dòng)控制器茄袖、請(qǐng)求及響應(yīng)的信息處理操软、視圖解析、本地化解析宪祥、上傳文件解析聂薪、異常處理以及表單標(biāo)簽綁定等內(nèi)容家乘。

1.3 體系結(jié)構(gòu)

Spring MVC是基于Model 2實(shí)現(xiàn)的技術(shù)框架。Spring MVC通過一個(gè)DispatcherServlet接收所有請(qǐng)求藏澳,并將具體工作委托給其他組件進(jìn)行處理仁锯。

Spring MVC體系結(jié)構(gòu)

  1. 客戶端發(fā)出一個(gè)HTTP請(qǐng)求,Web應(yīng)用服務(wù)器接收到這個(gè)請(qǐng)求翔悠,如果匹配DispatcherServlet的請(qǐng)求映射路徑(在web.xml中指定)业崖,Web容器將該請(qǐng)求轉(zhuǎn)交給DispatcherServlet處理。
  2. DispatcherServlet接收到這個(gè)請(qǐng)求后蓄愁,將根據(jù)請(qǐng)求的信息(包括URL双炕、HTTP方法、請(qǐng)求報(bào)文頭撮抓、請(qǐng)求參數(shù)妇斤、Cookie等)及HandlerMapping的配置找到處理請(qǐng)求的處理器(Handler)〉ふ可將HandlerMapping看成路由控制器趟济,將Handler看成目標(biāo)主機(jī)。值得注意的是:Spring MVC中并沒有定義一個(gè)Handler接口咽笼,實(shí)際上任何一個(gè)Object都可以成為請(qǐng)求處理器顷编。
  3. 當(dāng)DispatcherServlet根據(jù)HandlerMapping得到對(duì)應(yīng)當(dāng)前請(qǐng)求的Handler后,通過HandlerAdapter對(duì)Handler進(jìn)行封裝剑刑,再以統(tǒng)一的適配器接口調(diào)用Handler媳纬。 HandlerAdapter是Spring MVC的框架級(jí)接口,顧名思義HandlerAdapter是一個(gè)適配器施掏,它用統(tǒng)一的接口對(duì)各種Handler方法進(jìn)行調(diào)用钮惠。
  4. 處理器完成業(yè)務(wù)邏輯的處理后將運(yùn)回一個(gè)ModelAndView給DispatcherServlet,ModelAndView包含了視圖邏輯名和模型數(shù)據(jù)信息七芭。
  5. ModelAndView中包含的是“邏輯視圖名”而非真正的視圖對(duì)象素挽,DispatcherServlet借由ViewResolver完成邏輯視圖名到真實(shí)視圖對(duì)象的解析工作。
  6. 當(dāng)?shù)玫秸鎸?shí)的視圖對(duì)象View后狸驳,DispatcherServlet就使用這個(gè)View對(duì)象對(duì)ModelAndView中的模型數(shù)據(jù)進(jìn)行視圖渲染预明。
  7. 最終客戶端得到的響應(yīng)消息,可能是一個(gè)普通的HTML頁而耙箍,也可能是一個(gè)XML或JSON串撰糠, 甚至是一張圖片或一個(gè)PDF文檔等不同的媒體形式。

1.4 配置DispatcherServlet

可以在web.xml中配置一個(gè)Servlet辩昆,并通過<servlet-mapping>指定其處理的URL阅酪。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

    <!-- (1)從類路徑下加載Spring配置文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-context.xml</param-value>
    </context-param>

    <!-- (2)負(fù)責(zé)啟動(dòng) Spring 容器的監(jiān)聽器,它將引用(1)處的上下文參數(shù)獲得Spring配置文件的地址 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- (3)配置DispatcherServlet -->
    <servlet>
        <servlet-name>web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- (4)指定處理的URL路徑 -->
    <servlet-mapping>
        <servlet-name>web</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  1. 在(1)處,通過contextConfigLocation參數(shù)指定業(yè)務(wù)層Spring容器的配置文件(多個(gè)配置文件用,分割)术辐。
  2. 在(2)處砚尽,ContextLoaderListener是一個(gè)ServletLoaderListener,它通過contextConfigLocation指定的Spring配置文件啟動(dòng)業(yè)務(wù)層的Spring容器辉词。
  3. 在(3)處尉辑,配置了名為web的DispatcherServlet,它默認(rèn)加載/WEB-INF/web-servlet.xml(<servlet-name>-servlet.xml)的Spring配置文件较屿,啟動(dòng)Web層的Spring容器隧魄。Web層容器將作為業(yè)務(wù)層容器的子容器,Web層容器可以訪問業(yè)務(wù)層容器的Bean隘蝎,而業(yè)務(wù)層容器訪問不了Web層容器的Bean购啄。
  4. 在(4)處,通過<servlet-mapping>指定DispatcherServlet處理/*全部的HTTP請(qǐng)求嘱么。一個(gè)web.xml可以配置多個(gè)DispatcherServlet狮含,通過其對(duì)應(yīng)的<servlet-mapping>配置,讓每個(gè)DispatcherServlet處理不同的請(qǐng)求曼振。
DispatcherServlet 的配置參數(shù)

可以通過<servlet>的<init-param>屬性指定配置參數(shù):

  1. namespace參數(shù):DispatcherServlet對(duì)應(yīng)的命名空間几迄,默認(rèn)是WEB-INF/<servlet-name>-servlet.xml。在顯式配置該參數(shù)后冰评,新的配置文件對(duì)應(yīng)的路徑是WEB-INF/<namespace>.xml映胁,例如如果將namespace設(shè)置為sample,則對(duì)應(yīng)的Spring配置文件為WEB-INF/sample.xml甲雅。
  2. contextConfigLocation:如果DispatcherServlet上下文對(duì)應(yīng)的Spring配置文件有多個(gè)解孙,則可以使用該屬性按照Spring資源路徑的方式指定,如classpath:sample1.xml,classpath:sample2.xml抛人。
  3. publishContext:默認(rèn)為true弛姜。DispatcherServlet根據(jù)該屬性決定是否將WebApplicationContext發(fā)布到ServletContext的屬性列表中,方便調(diào)用者可借由ServletContext找到WebApplicationContext實(shí)例妖枚,對(duì)應(yīng)的屬性名為DispatcherServlet#getServletContextAttributeName()的返回值廷臼。
  4. publishEvents:默認(rèn)為true。當(dāng)DispatcherServlet處理完一個(gè)請(qǐng)求后绝页,是否需要向容器發(fā)布一個(gè)ServletRequestHandleEvent事件荠商。
Spring容器配置
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <context:component-scan base-package="com.ankeetc.web"/>
    <!-- 會(huì)自動(dòng)注冊(cè)RequestMappingHandlerMapping與RequestMappingHandlerAdapter兩個(gè)Bean,這是SpringMVC為@Controllers分發(fā)請(qǐng)求所必需的 -->
    <!-- 并提供了數(shù)據(jù)綁定支持抒寂、@NumberFormatannotation支持结啼、 @DateTimeFormat支持掠剑、@Valid支持屈芜、讀寫XML的支持和讀寫JSON的支持等功能属铁。 -->
    <mvc:annotation-driven />

</beans>

1.5 基于編程的配置

Spring 4.0已經(jīng)全面支持Servlet 3.0例嘱,可以使用編程的方式配置Servlet容器腋腮。在Servlet 3.0環(huán)境中袜刷,容器會(huì)在類路徑中查找實(shí)現(xiàn)javax.servlet.ServletContainerInitializer接口的類钢拧,如果發(fā)現(xiàn)實(shí)現(xiàn)類膜钓,就會(huì)用它來配置Servlet容器沃疮。Spring提供了這個(gè)接口的實(shí)現(xiàn)肺缕,名為SpringServletContainerInitializer泉手,這個(gè)類反過來又查找實(shí)現(xiàn)WebApplicationInitializer的類并將配置的任務(wù)交給它們來完成。Spring還提供了一個(gè)WebApplicationInitializer基礎(chǔ)實(shí)現(xiàn)類AbstractAnnotationConfigDispatcherServletInitializer内狸,使得它在注冊(cè)DispatcherServlet時(shí)只需要簡(jiǎn)單地指定它的Servlet映射即可。

public class WebApplicationInitilalizer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

1.6 DispatcherServlet的內(nèi)部邏輯

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

DispatcherServlet#initStrategies()方法將在WebApplicationContext初始化后執(zhí)行,此時(shí)Spring上下文中的Bean已經(jīng)初始化完畢安皱,該方法通過反射查找并裝配Spring容器中用戶自定義的Bean居砖,如果找不到就裝配默認(rèn)的組件實(shí)例。

默認(rèn)組件

在DispatcherServlet.properties配置文件里邊唇敞,指定了DispatcherServlet所使用的默認(rèn)組件蔗草。如果用戶希望采用非默認(rèn)的組件,只需在Spring配置文件中配置自定義的組件Bean即可疆柔。

# 本地化解析器
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.annotation.DefaultAnnotationHandlerMapping

# 處理器適配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

# 異常處理器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
    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
DispatcherServlet裝配各型組件的邏輯

DispatcherServlet裝配各型組件的邏輯

2 注解驅(qū)動(dòng)的控制器

2.1 @RequestMapping映射請(qǐng)求

  1. 在POJO類上標(biāo)注@Controller咒精,再通過<context:component-scan>掃描到該類,可以是POJO成為一個(gè)能處理HTTP請(qǐng)求的控制器旷档。
  2. 在控制器的類定義和方法定義處都可以使用@RequestMapping映射對(duì)應(yīng)的處理方法模叙。
  3. @RequestMapping不但支持標(biāo)準(zhǔn)的URL,還支持Ant風(fēng)格和{XXX}占位符的URL鞋屈。
  4. @RequestMapping和value范咨、method、params及headers分別表示請(qǐng)求路徑厂庇、請(qǐng)求方法渠啊、請(qǐng)求參數(shù)及報(bào)文頭的映射條件。

2.2 獲取請(qǐng)求內(nèi)容

  1. 可以通過@RequestParam权旷、@RequestHeader昭抒、@PathVariable獲取HTTP請(qǐng)求信息。
  2. 可以使用@CookieValue讓方法入?yún)⒔壎硞€(gè)Cookie值
  3. 可以使用@MatrixVariable注解將請(qǐng)求中的矩陣變量綁定到處理器的方法參數(shù)中炼杖。
  4. 可以使用命令/表單對(duì)象(就是一個(gè)POJO)綁定請(qǐng)求參數(shù)值灭返,Spring會(huì)按照請(qǐng)求參數(shù)名和對(duì)象屬性名匹配的方式,自動(dòng)為該對(duì)象填充屬性坤邪。
  5. 可以使用Servlet API的類作為處理方法的入?yún)⑽鹾鏗ttpServletRequest、HttpServletResponse艇纺、HttpSession怎静;如果使用HttpServletResponse返回相應(yīng)邮弹,則處理方法返回著設(shè)置成void即可;在org.springframework.web.context.request定義了若干個(gè)可代理Servlet原生API類的接口蚓聘,如WebRequest和NativeWebRequest腌乡。
  6. 可以使用java.io中的InputStream、Reader夜牡、OutputStream与纽、Writer作為方法的入?yún)ⅰ?/li>
  7. 還可以使用java.util.Locale、java.security.Principal作為入?yún)ⅰ?/li>

2.3 使用HttpMessageConverter

HttpMessageConverter接口可以將請(qǐng)求信息轉(zhuǎn)換為一個(gè)對(duì)象(類型為T)塘装,并將對(duì)象(類型為T)綁定到請(qǐng)求方法的參數(shù)中或輸出為響應(yīng)信息急迂。DispatcherServlet默認(rèn)已經(jīng)安裝了RequestMethodHandlerAdapter作為HandlerAdapter組件的實(shí)現(xiàn)類,HttpMessageConverter即由RequestMethodHandlerAdapter使用蹦肴,將請(qǐng)求信息轉(zhuǎn)換為對(duì)象僚碎,或?qū)?duì)象轉(zhuǎn)換為響應(yīng)信息。

HttpMessageConverter的實(shí)現(xiàn)類

Spring為HttpMessageConverter提供了眾多的實(shí)現(xiàn)類:


實(shí)現(xiàn)類

實(shí)現(xiàn)類

實(shí)現(xiàn)類
默認(rèn)的HttpMessageConverter

RequestMappingHandlerAdapter已經(jīng)默認(rèn)裝配了以下的HttpMessageConverter:

  • StringHttpMessageConverter
  • ByteArrayHttpMessageConverter
  • SourceHttpMessageConverter
  • AllEncompassingFormHttpMessageConverter
裝配其他類型的HttpMessageConverter

如果需要裝配其他類型的HttpMessageConverter阴幌,可以在Spring的Web容器上下文中自行定義一個(gè)RequestMappingHandlerAdapter勺阐,注冊(cè)若干HttpMessageConverter。如果在Spring web容器中顯式定義了一個(gè)RequestMappingHandlerAdapter矛双,則Spring MVC將使用它覆蓋默認(rèn)的RequestMappingHandlerAdapter渊抽。

使用HttpMessageConverter
  1. 可以使用@RequestBody、@ResponseBody對(duì)處理方法進(jìn)行標(biāo)注
  2. 可以使用HttpEntity<T>背零、ResponseEntity<T>作為處理方法的入?yún)⒒蚍祷刂?/li>

RestTemplate是Spring的模板類腰吟,可以使用該類調(diào)用Web服務(wù)端的服務(wù),它支持Rest風(fēng)格的URL徙瓶。

結(jié)論
  1. 當(dāng)控制器處理方法使用到@RequestBody毛雇、@ResponseBody 或 HttpEntity<T>、ResponseEntity<T> 時(shí)侦镇,Spring MVC才會(huì)使用注冊(cè)的HttpMessageConvertor對(duì)請(qǐng)求灵疮、相應(yīng)消息進(jìn)行處理。
  2. 當(dāng)控制器處理方法使用到@RequestBody壳繁、@ResponseBody 或 HttpEntity<T>震捣、ResponseEntity<T>時(shí),Spring 首先根據(jù)請(qǐng)求頭或響應(yīng)的Accept屬性選擇匹配的 HttpMessageConverter闹炉,進(jìn)而根據(jù)參數(shù)類型或泛型類型的過濾得到匹配的 HttpMessageConverter蒿赢,若找不到可用的 HttpMessageConverter 將報(bào)錯(cuò)。
  3. @RequestBody渣触、@ResponseBody不需要成對(duì)出現(xiàn)羡棵。
處理XML和JSON

Spring MVC提供了幾個(gè)處理XML和JSON格式的請(qǐng)求、響應(yīng)消息的HttpMessageConverter:

  • MarshallingHttpMessageConverter:處理XML
  • Jaxb2RootElementHttpMessageConverter:處理XML嗅钻,底層使用JAXB
  • MappingJackson2HttpMessageConverter:處理JSON格式

只要在Spring Web容器中為RequestMappingHandlerAdapter裝配好相應(yīng)的HttpMessageConverter皂冰,并在交互中通過請(qǐng)求的Accept指定MIME類型店展,Spring MVC就可以是服務(wù)器段的處理方法和客戶端透明的通過XML或JSON格式進(jìn)行通信。

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            </list>
        </property>
    </bean>

2.4 使用@RestController

@RestController已經(jīng)標(biāo)注了@ResponseBody和@Controller秃流,可以直接在控制器上標(biāo)注該注解赂蕴,就不用在每個(gè)@RequestMapping方法上添加@ResponseBody了。

2.5 AsyncRestTemplate

Spring 4.0提供了AsyncRestTemplate用于以異步無阻塞的方式進(jìn)行服務(wù)訪問舶胀。

public class WebApplicationInitilalizer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
        registration.setLoadOnStartup(1);
        // 此處要設(shè)置為true
        registration.setAsyncSupported(true);
        registration.addMapping("/");
    }
}

@RestController
public class AsyncController {

    @RequestMapping(value = "/async", method = RequestMethod.GET)
    public Callable<String> async() {
        System.out.println("hello!");
        return new Callable<String>() {
            @Override
            public String call() throws Exception {
                TimeUnit.SECONDS.sleep(5);
                return "ASYNC";
            }
        };
    }
}
public class Main {
    public static void main(String[] args) {
        AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();

        ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.getForEntity("http://localhost:8080/async", String.class);

        System.out.println("return");
        future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
            @Override
            public void onFailure(Throwable ex) {
                System.out.println("Failure");
            }

            @Override
            public void onSuccess(ResponseEntity<String> result) {
                System.out.println("Success");
            }
        });
    }
}

2.6 處理模型數(shù)據(jù)

Spring MVC提供了多種途徑輸出模型數(shù)據(jù):

  1. ModelAndView:當(dāng)處理方法返回值類型為ModelAndView時(shí)概说,方法體即可通過該對(duì)象添加模型數(shù)據(jù);
  2. @ModelAttribute:在方法入?yún)?biāo)注該注解后峻贮,入?yún)⒌膶?duì)象就會(huì)放到數(shù)據(jù)模型中席怪;
  3. Map和Model:如果方法入?yún)閛rg.framework.ui.Model应闯、org.framework.ui.ModelMap纤控、java.util.Map,當(dāng)處理方法返回時(shí)碉纺,Map中的數(shù)據(jù)會(huì)自動(dòng)添加到模型中船万;
  4. @SessionAttributes:將模型中的某個(gè)屬性暫存到HttpSession中,以便多個(gè)請(qǐng)求之間可以共享這個(gè)屬性骨田。

3 處理方法的數(shù)據(jù)綁定

Spring會(huì)根據(jù)請(qǐng)求方法簽名的不同耿导,將請(qǐng)求中的信息以一定方式轉(zhuǎn)換并綁定到請(qǐng)求方法的入?yún)⒅校€會(huì)進(jìn)行數(shù)據(jù)轉(zhuǎn)換态贤、數(shù)據(jù)格式化及數(shù)據(jù)校驗(yàn)等舱呻。

3.1 數(shù)據(jù)綁定流程

數(shù)據(jù)綁定

Spring MVC通過反射對(duì)目標(biāo)簽名進(jìn)行分析,將請(qǐng)求消息綁定到處理方法的入?yún)⒅杏破?shù)據(jù)綁定的核心部件是DataBinder箱吕。Spring MVC主框架將ServletRequest對(duì)象及處理方法的入?yún)?duì)象實(shí)例傳遞給 DataBinder,DataBinder 首先調(diào)用裝配在 Spring Web 上下文中的 ConversionService 組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換柿冲、數(shù)據(jù)格式化等工作茬高,將ServletRequest中的消息填充到入?yún)?duì)象中, 然后調(diào)用Validator組件對(duì)己經(jīng)綁定了請(qǐng)求消息數(shù)據(jù)的入?yún)?duì)象進(jìn)行數(shù)據(jù)合法性校驗(yàn)假抄,最 終生成數(shù)據(jù)綁定結(jié)果BindingResult對(duì)象怎栽。BindingResult包含了已完成數(shù)據(jù)綁定的入?yún)?對(duì)象,還包含相應(yīng)的校驗(yàn)錯(cuò)誤對(duì)象宿饱。Spring MVC抽取BindingResult中的入?yún)?duì)象及校驗(yàn)錯(cuò)誤對(duì)象熏瞄,將它們賦給處理方法的相應(yīng)入?yún)ⅰ?/p>

3.2 數(shù)據(jù)轉(zhuǎn)換

類型轉(zhuǎn)換模塊位于org.framework.core.convert包中,同時(shí)由于歷史原因谬以,Spring還支持JDK的PropertyEditor强饮。

ConversionService簡(jiǎn)介

ConversionService 是 Spring 類型轉(zhuǎn)換體系的核心接口,它定義了以下4個(gè)方法:

  • boolean canConvert(Class<?> sourceType, Class<?> targetType):判斷是否可以將一個(gè)Java類轉(zhuǎn)換為另一個(gè)Java類蛉签。
  • Boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType):需轉(zhuǎn)換的類將以成員變量的方式出現(xiàn)在宿主類中胡陪。TypeDescriptor不但描述了需轉(zhuǎn)換類的信息沥寥,還描述了從宿主類的上下文信息,如成員變量上的注解柠座,成員變量是否以數(shù)組邑雅、集合或Map的方式呈現(xiàn)等。類型轉(zhuǎn)換邏輯可以利用這些信息做出 各種靈活的控制妈经。
  • <T> T convert(Object source, Class<T> targetType):將原類型對(duì)象轉(zhuǎn)換為目標(biāo)類型對(duì)象淮野。
  • Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType):
    將對(duì)象從原類型對(duì)象轉(zhuǎn)換為目標(biāo)類型對(duì)象,此時(shí)往往會(huì)用到所在宿主類的上下 文信息吹泡。

第一個(gè)和第三個(gè)接口方法類似于PmpertyEditor骤星,它們不關(guān)注類型對(duì)象所在的上下文 信息,只簡(jiǎn)單地完成兩個(gè)類型對(duì)象的轉(zhuǎn)換爆哑,唯一的區(qū)別在于這兩個(gè)方法支持任意兩個(gè)類型的轉(zhuǎn)換洞难。而第二個(gè)和第四個(gè)接口方法會(huì)參考類型對(duì)象所在宿主類的上下文信息,并利用這些信息進(jìn)行類型轉(zhuǎn)換揭朝。

使用ConversionService

可以利用 org.springframework.context.support.ConversionServiceFactoryBean 在 Spring 的 上下文中定義一個(gè)ConversionService队贱。Spring將自動(dòng)識(shí)別出上下文中的ConversionService, 并在Bean屬性配置及Spring MVC處理方法入?yún)⒔壎ǖ葓?chǎng)合使用它進(jìn)行數(shù)據(jù)轉(zhuǎn)換潭袱。該FactoryBean創(chuàng)建ConversionService內(nèi)建了很多轉(zhuǎn)換器柱嫌,可完成大多數(shù)Java類型的轉(zhuǎn)換工作。除了包括將String對(duì)象轉(zhuǎn)換為各種基礎(chǔ)類型的對(duì)象外屯换,還包括String编丘、 Number、Array彤悔、Collection嘉抓、Map、Properties 及 Object 之間的轉(zhuǎn)換器蜗巧≌泼撸可通過ConversionServiceFactoryBean的converters屬性注冊(cè)自定義的類型轉(zhuǎn)換器:

    <bean class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.ankeetc.MyConverter"/>
            </list>
        </property>
    </bean>
Spring支持的轉(zhuǎn)換器

Spring 在 org.springframework.core.convert.converter 包中定義了3種類型的轉(zhuǎn)換器接口,實(shí)現(xiàn)任意一個(gè)轉(zhuǎn)換器接口都可以作為自定義轉(zhuǎn)換器注冊(cè)到ConversionServiceFactoryBean中幕屹。這3種類型的轉(zhuǎn)換器接口分別為:

  • Converter<S, T>:將S類型的對(duì)象轉(zhuǎn)換為T類型的對(duì)象
  • GenericConverter:根據(jù)源類對(duì)象及目標(biāo)類對(duì)象所在的宿主類的上下文信息進(jìn)行類型轉(zhuǎn)換工作蓝丙。該類還有一個(gè)子接口ConditionalGenericConverter,它添加了一個(gè)接口方法根據(jù)源類型及目標(biāo)類型所在宿主類的上下文信息決定是否要進(jìn)行類型轉(zhuǎn)換望拖。
  • ConverterFactory:

ConversionServiceFactoryBean 的 converters 屬性可接受 Converter渺尘、ConverterFactory、 GenericConverter或ConditionalGenericConverter接口的實(shí)現(xiàn)類说敏,并把這些轉(zhuǎn)換器的轉(zhuǎn)換邏輯統(tǒng)一封裝到一個(gè) ConversionService 實(shí)例對(duì)象中(GenericConversionService)鸥跟。Spring 在Bean屬性配置及Spring MVC請(qǐng)求消息綁定時(shí)將利用這個(gè)ConversionService實(shí)例完成類型轉(zhuǎn)換工作。

在Spring中使用@lnitBinder 和 WebBindingInitializer裝配自定義編輯器

Spring也支持JavaBeans的PropertyEditor∫阶桑可以在控制器中使用@InitBinder添加自定義的編輯器枫匾,也可以通過 WebBindingInitializer 裝配在全局范圍內(nèi)使用的編輯器。

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(User.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                User user = new User();
                user.setName(text);

                this.setValue(user);
            }
        });
    }

如果希望在全局范圍內(nèi)使用拟淮,則可實(shí)現(xiàn)WebBindingInitializer接口并在該實(shí)現(xiàn)類中注冊(cè)干茉。

  1. 實(shí)現(xiàn)WebBindingInitializer接口并在initBinder接口方法中注冊(cè)了自定義的編輯器。
  2. 在 Spring 上下文中通過 RequestMappingHandlerAdapter 裝配自定義的Initializer很泊。
順序

對(duì)于同一個(gè)類型對(duì)象來說角虫,如果既在ConversionService中裝配了自定義轉(zhuǎn)換器,又通過WebBindinglnitializer裝配了自定義編輯器委造,同時(shí)還在控制器中通過@InitBinder裝 配了自定義編輯器戳鹅,那么Spring MVC將按以下優(yōu)先順序查找對(duì)應(yīng)類型的編輯器:

  1. 查詢通過@InitBinder裝配的自定義編輯器。
  2. 查詢通過ConversionService裝配的自定義轉(zhuǎn)換器昏兆。
  3. 查詢通過WebBindingInitializer裝配的自定義編輯器枫虏。

3.3 數(shù)據(jù)格式化

Spring的轉(zhuǎn)換器并不提供輸入及輸出信息格式化的工作,一般需要轉(zhuǎn)換的源類型數(shù)據(jù)(一般是字符串)都是具有一定格式的亮垫,在不同的本地化環(huán)境中模软, 同一類型的數(shù)據(jù)還會(huì)相應(yīng)地呈現(xiàn)不同的顯示格式伟骨。Spring引入了一個(gè)新的格式化框架饮潦,這個(gè)框架位于org.springframework.format類包中。

最重要的 Formatter<T>接口
注解驅(qū)動(dòng)格式化AnnotationFormatterFactory

為了讓注解和格式化的屬性類型關(guān)聯(lián)起來携狭,Spring在Formatter<T>所在的包中還提供了一個(gè) AnnotationFormatterFactory<A extends Annotation>接口继蜡。

啟用注解驅(qū)動(dòng)格式化功能

對(duì)屬性對(duì)象的輸入/輸出進(jìn)行格式化,從本質(zhì)上講依然屬于“類型轉(zhuǎn)換”的范疇逛腿。 Spring就是基于對(duì)象轉(zhuǎn)換框架植入“格式化”功能的稀并。Spring 在格式化模塊中定義了一個(gè)實(shí)現(xiàn) ConversionService 接口的 FormattingConversionService實(shí)現(xiàn)類,該實(shí)現(xiàn)類擴(kuò)展了 GenericConversionService单默,因此它既具有類型轉(zhuǎn)換功能碘举,又具有格式化功能。
FormattingConversionService 也擁有一個(gè)對(duì)應(yīng)的 FormattingConversionServiceFactoryBean 工廠類搁廓,后者用于在Spring上下文中構(gòu)造一個(gè)FormattingConversionService引颈。通過這個(gè)工廠類,既可以注冊(cè)自定義的轉(zhuǎn)換器境蜕,還可以注冊(cè)自定義的注解驅(qū)動(dòng)邏輯蝙场。由于 FormattingConversionServiceFactoryBean 在內(nèi)部會(huì)自動(dòng)注冊(cè) NumberFormatAnnotationFormatterFactory 和 JodaDateTimeFormatAnnotationFormatterFactory,因此裝配了 FormattingConversionServiceFactoryBean 后粱年,就可以在 Spring MVC 入?yún)⒔壎澳P蛿?shù)據(jù)輸出時(shí)使用注解驅(qū)動(dòng)的格式化功能售滤。
值得注意的是,<mvc:annotation-driven/>標(biāo)簽內(nèi)部默認(rèn)創(chuàng)建的ConversionService實(shí)例就是一個(gè) FormattingConversionServiceFactoryBean。

3.4 數(shù)據(jù)校驗(yàn)

Spring擁有自己獨(dú)立的數(shù)據(jù)校驗(yàn)框架完箩,同時(shí)支持JSR-303標(biāo)準(zhǔn)的校驗(yàn)框架赐俗。Spring 的DataBinder在進(jìn)行數(shù)據(jù)綁定時(shí),可同時(shí)調(diào)用校驗(yàn)框架完成數(shù)據(jù)校驗(yàn)工作弊知。在Spring MVC中秃励,則可直接通過注解驅(qū)動(dòng)的方式進(jìn)行數(shù)據(jù)校驗(yàn)。
LocalValidatorFactoryBean 既實(shí)現(xiàn)了 Spring 的 Validator 接口吉捶,又實(shí)現(xiàn)了 JSR-303 的 Validator 接口夺鲜。只要在 Spring 容器中定義了一個(gè) LocalValidatorFactoryBean,即可將其注入需要數(shù)據(jù)校驗(yàn)的Bean中呐舔。值得注意的是币励,Spring本身沒有提供JSR-303的實(shí)現(xiàn),所以必須將JSR-303的實(shí)現(xiàn) 者(如Hibernate Validator)的JAR文件放到類路徑下珊拼,Spring將自動(dòng)加載并裝配好 JSR-303的實(shí)現(xiàn)者食呻。
<mvc:annotation-driven/>會(huì)默認(rèn)裝配一個(gè) LocalValidatorFactoryBean,通過在處理方法的入?yún)⑸蠘?biāo)注@Valid注解澎现,即可讓Spring MVC在完成數(shù)據(jù)綁定后執(zhí)行數(shù)據(jù)校驗(yàn)工作仅胞。

4 視圖和視圖解析器

5 本地化

Spring提供了以下4個(gè)本地化解析器。

  • AcceptHeaderLocaleResolver:根據(jù) HTTP 報(bào)文頭的 Accept-Language 參數(shù)確定本 地化類型剑辫。如果沒有顯式定義本地化解析器干旧,則Spring MVC默認(rèn)采用 AcceptHeaderLocaleResolver。
  • CookieLocaleResolver:根據(jù)指定的Cookie值確定本地化類型妹蔽。
  • SessionLocaleResolver:根據(jù)Session中特定的屬性值確定本地化類型椎眯。
  • LocaleChangeInterceptor:從請(qǐng)求參數(shù)中獲取本次請(qǐng)求對(duì)應(yīng)的本地化類型。

6 文件上傳

Spring MVC為文件上傳提供了直接支持胳岂,這種支持是通過即插即用的MultipartResolver 實(shí)現(xiàn)的编整。Spring 使用 Jakarta Commons FileUpload 技術(shù)實(shí)現(xiàn)了一個(gè) MultipartResolver 實(shí)現(xiàn) 類:CommonsMultipartResolver。
在Spring MVC上下文中默認(rèn)沒有裝配MultipartResolver,因此默認(rèn)情況下不能 處理文件的上傳工作乳丰。如果想使用Spring的文件上傳功能掌测,則需要先在上下文中配置 MultipartResolver。

7 WebSocket

8 靜態(tài)資源處理

  1. <mvc:default-servlet-handler/>:在 smart-servlet.xml 中配置<mvc:default-servlet-handler/>后产园,會(huì)在 Spring MVC 上下文中定義一個(gè) org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler汞斧,它將充當(dāng)一個(gè)檢查員的角色,對(duì)進(jìn)入DispatcherServlet的URL進(jìn)行篩查淆两。如果發(fā)現(xiàn)是靜態(tài)資源的請(qǐng)求断箫,就將該請(qǐng)求轉(zhuǎn)由Web應(yīng)用服務(wù)器默認(rèn)的Servlet處理;如果不是靜態(tài)資源 的請(qǐng)求秋冰,則由DispatcherServlet繼續(xù)處理仲义。
  2. <mvc:resources/><mvc:default-servlet-handler/>將靜態(tài)資源的處理經(jīng)由Spring MVC框架交回Web應(yīng) 用服務(wù)器。而<mvc:resources/>更進(jìn)一步,由SpringMVC框架自己處理靜態(tài)資源埃撵,并添 加一些有用的附加功能赵颅。

9 攔截器

當(dāng)收到請(qǐng)求時(shí),DispatcherServlet將請(qǐng)求交給處理器映射(HandlerMapping)暂刘,讓它找出對(duì)應(yīng)該請(qǐng)求的HandlerExecutionChain對(duì)象饺谬。在講解HandlerMapping之前,有必要 認(rèn)識(shí)一下這個(gè) HandlerExecutionChain 對(duì)象谣拣。

HandlerExecutionChain

HandlerExecutionChain負(fù)責(zé)處理請(qǐng)求并返回ModelAndView的處理執(zhí)行鏈募寨,它包含一個(gè)處理該請(qǐng)求的處理器 (Handler),同時(shí)包括若干個(gè)對(duì)該請(qǐng)求實(shí)施攔截的攔截器(HandlerInterceptor)森缠。當(dāng) HandlerMapping 返回 HandlerExecutionChain 后拔鹰,DispatcherServlet 將請(qǐng)求交給定義在 HandlerExecutionChain中的攔截器和處理器一并處理。
位于處理器鏈末端的是一個(gè) Handler贵涵,DispatcherServlet通過 Handler Adapter適配器對(duì) Handler進(jìn)行封裝列肢,并按統(tǒng)一的適配器接口對(duì) Handler處理方法進(jìn)行調(diào)用”雒可以在 web-servlet.xml中配置多個(gè)攔截器,每個(gè)攔截器都可以指定一個(gè)匹配的映射路徑瓷马,以限制攔截器的作用范圍。

10 異常處理

Spring MVC通過 HandlerExceptionResolver處理程序的異常跨晴,包括處理器映射欧聘、數(shù)據(jù)綁定及處理器執(zhí)行時(shí)發(fā)生的異常。 HandlerExceptionResolver僅有一個(gè)接口方法:Modelandview resolveException(HttpServletRequest request HttpServletResponse response Object handler, Exception ex)坟奥。當(dāng)發(fā)生異常時(shí)树瞭,Spring MVC將調(diào)用 resolveException方法,并轉(zhuǎn)到 ModelAndView 對(duì)應(yīng)的視圖中爱谁,作為一個(gè)異常報(bào)告頁面反饋給用戶。

實(shí)現(xiàn)類

HandlerExceptionResolver擁有4個(gè)實(shí)現(xiàn)類

  1. DefaultHandlerExceptionResolver:默認(rèn)裝配了該類孝偎,將對(duì)應(yīng)異常轉(zhuǎn)換為錯(cuò)誤碼
  2. SimpleMappingExceptionResolver:對(duì)所有異常進(jìn)行統(tǒng)一處理
  3. AnnotationMethodHandlerExceptionResolver:默認(rèn)注冊(cè)了該類访敌,允許通過@ExceptionHandler注解指定處理特定的異常
  4. ResponseStatusExceptionResolver
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市衣盾,隨后出現(xiàn)的幾起案子寺旺,更是在濱河造成了極大的恐慌,老刑警劉巖势决,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阻塑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡果复,警方通過查閱死者的電腦和手機(jī)陈莽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人走搁,你說我怎么就攤上這事独柑。” “怎么了私植?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵忌栅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我曲稼,道長(zhǎng)索绪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任贫悄,我火速辦了婚禮者春,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘清女。我一直安慰自己钱烟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布嫡丙。 她就那樣靜靜地躺著拴袭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曙博。 梳的紋絲不亂的頭發(fā)上拥刻,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音父泳,去河邊找鬼般哼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惠窄,可吹牛的內(nèi)容都是我干的蒸眠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼杆融,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼楞卡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脾歇,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤蒋腮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后藕各,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體池摧,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年激况,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了作彤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膘魄。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宦棺,靈堂內(nèi)的尸體忽然破棺而出瓣距,到底是詐尸還是另有隱情,我是刑警寧澤代咸,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布蹈丸,位于F島的核電站,受9級(jí)特大地震影響呐芥,放射性物質(zhì)發(fā)生泄漏逻杖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一思瘟、第九天 我趴在偏房一處隱蔽的房頂上張望荸百。 院中可真熱鬧,春花似錦滨攻、人聲如沸够话。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽女嘲。三九已至,卻和暖如春诞帐,著一層夾襖步出監(jiān)牢的瞬間欣尼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工停蕉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愕鼓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓慧起,卻偏偏與公主長(zhǎng)得像菇晃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子完慧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容