spring MVC -- controller引數的解析

weixin_33766168發表於2016-05-21

spring給我們帶來了什麼?

spring IoC、AOP、Transaction這些都是很重要的特性,但是這篇這些都不是主角,主要來談談springMVC是如何對請求引數進行解析封裝的,以及簡單的介紹spring是怎麼進行http請求路由的。

0x01 springMVC工程的建立

本篇就不過多介紹springMVC的工程的新建過程,此次除錯springMVC原始碼新建的工程目錄如下,通過maven管理的工程:
圖片描述

0x02 spring是如何接入web容器的

在進行springMVC的開發過程中我們都會在web.xml檔案中進行下面一些配置,但是這些配置都是用來幹嘛的呢?

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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_2_5.xsd">
    <!-- Spring配置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- logback配置 LogbackConfigListener由logback-ext-spring提供 -->
    <context-param>
        <param-name>logbackConfigLocation</param-name>
        <param-value>classpath:logconfig/logback.xml</param-value>
    </context-param>
    <listener>
        <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
    </listener>

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/applicationMvcContext.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app> 

這裡著重介紹ContextLoaderListener和DispatcherServlet。

  1. ContextLoaderListener是spring提供的類,為了在web容器中建立IoC容器而服務的。它實現了ServletContextListener這個介面,通過這個介面的方法實現servlet生命週期的回撥,在這個過程中會通過XmlWebApplicationContext去載入bean的配置建立IoC容器。

  2. DispatcherServlet作為一個前端控制器,他會去載入另一個bean的配置形成一個IoC容器,這個IoC會將ContextLoaderListener載入的IoC容器作為父容器,這樣的好處是從DispatcherServlet容器中getBean也能取到父容器中的bean,他會先去父容器中看有沒有,如果找到直接返回了。DispatcherServlet 主要處理HTTP的請求分發(HandlerMapping),對controller、viewresolver、view進行管理。DispatcherServlet接收到請求由HandlerMapping進行匹配,匹配成功後交由controller進行業務邏輯的處理,業務邏輯處理完成後交由viewresolver進行資料的解析同時找到對應的view,最終由DispatcherServlet將view的結果render到瀏覽器進行解析。

0x03 controller的引數是如何被解析的

  • 在開發過程中遇到下面這樣的controller方法怎麼也得到不到us值,

    public String printWelcome(ModelMap model,ArrayList<String> us){}

  • 如果改成下面這樣就可以正常得到us的值

    public String printWelcome(ModelMap model,String us){}

這是為什麼?怎麼解決?

  • 搜尋後發現這樣可以解決問題,但是sowhat?

    public String printWelcome(ModelMap model,@RequestParam("us[]") List<String> us){}

  • 所以需要來剖析下springMVC的原始碼了,發現spring是通過下面這個方法進行controller引數解析的。

    org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments

解析引數值的程式碼(裡面好多if else啊):
圖片描述

從這個圖片可以看到通過if去判斷paramName對應controller方法中的@RequestParam,pathVarName對應@PathVariable,沒有加任何修飾會進入到attrName這個分支裡面去解析引數。在解析的過程中會通過函式的引數型別去組裝物件傳遞到RequestMapping類的方法中。

  • 如果是下面這樣的方法引數又會怎樣

    public String printWelcome(ModelMap model,@RequestParam("us[]") List<User> us){}

  • User是自定義的引數,spring會對這種引數進行一個特殊的處理處理方法如下:

    org.springframework.beans.TypeConverterDelegate#convertIfNecessary

在這個方法中有一段處理的程式碼,會根據List內部元素型別去判斷型別是不準確並賦值。
圖片描述

到這裡應該和controller相關的註解可以隨便用了吧?

0x04 感想

在學校期間一直用著C、C++進行開發,其實也沒怎麼開發,只是作為裝逼的一種手段,如今踏入工作崗位開始離C、C++很遠了,又開始了0基礎JAVA開發。作為一個門外漢來觀望spring。
spring的東西太多了,也很權威大家基本上都用,所以後面還學要加強這方面的探索和學習,期待後面自己還能將spring的學習感想記錄下來。這篇是在進行開發的過程中遇到上面提到的一個問題所以想深入瞭解其背後的實現方式,未來繼續spring。

0x05 參考

在學習spring的過程中可以參考計文柯著的spring技術內幕,裡面結合程式碼講解了spring的各個重要的技術,需要一定的基礎再去看比較好,值得推薦。

相關文章