第三十六章:基於SpringBoot架構重寫SpringMVC請求參數裝載

在國內企業開發項目中大多數都已經偏向Spring家族式的開發風格,在前幾年國內項目都是以Structs2做爲Web開發的主導,不過因爲近幾年發生的事情確實讓開發者對它失去了以往的信心。與此同時Spring家族發佈了SpringMVC,並且完美的整合Spring來開發企業級大型Web項目。它有着比Structs2更強大的技術支持以及更靈活的自定義配置,接下來咱們就看看本章的內容,咱們自定義實現SpringMVC參數綁定規則,根據業務定製參數裝載實現方式。html

本章目標

根據項目定製SpringMVC參數狀態並瞭解SpringMVC的裝載過程以及實現方式。java

構建項目

咱們先來建立一個SpringBoot項目,添加本章所需的依賴,pom.xml配置文件以下所示:git

...//省略部分配置
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring boot tomcat jsp 支持開啓 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--servlet支持開啓-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!-- jstl 支持開啓 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!--lombok支持-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--fastjson支持-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.38</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
...//省略部分配置

本章須要JSP相關的依賴支持,因此須要添加對應的依賴,修改application.properties配置文件讓JSP生效,配置內容以下所示:web

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

相關JSP配置能夠訪問第二章:SpringBoot與JSP間不可描述的祕密查看講解。spring

SpringMVC的參數裝載

在講解咱們自定義參數裝載以前,咱們先來看看SpringMVC內部爲咱們提供的參數裝載方式。數據庫

添加測試JSP

咱們首先來添加一個測試的jsp頁面,頁面上添加一些輸入元素,代碼以下所示:apache

<%--
  Created by IntelliJ IDEA.
  User: hengyu
  Date: 2017/9/17
  Time: 10:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form method="post" action="/submit">
        教師姓名:<input type="text" name="name"/><br/><br/>
        學生姓名:<input type="text" name="name"/><br/><br/>
        學生年齡:<input type="text" name="age"/><br/><br/>
        <input type="submit"/>
    </form>
</body>
</html>

index.jsp內添加了三個name的文本輸入框,若是咱們如今提交到後臺SpringMVC爲默認爲咱們解析成一個數組,若是根據描述而言的來處理則是不合理的,固然也可使用各類手段完成字段參數的裝載,好比:爲教師的name添加一個數組或者List集合進行接受,這種方式也是能夠實現但不優雅json

若是大家項目組有嚴格的開發規範要求,這種方式是不容許出如今Controller方法內的。api

那這個問題就讓人頭疼了,在以前咱們使用Struct2的時候是能夠根據指定的前綴,如:xxx.xxx來進行映射的,而SpringMVC並無提供這個支持,不過它提供了自定義參數裝載的實現方法,那就沒有問題了,咱們能夠手寫。數組

自定義的參數裝載

既然上面的代碼實現知足不了咱們的需求,那麼我接下來就來重寫參數裝載。

建立ParameterModel註解

對於一直使用SpringMVC的朋友來講,應該對@RequestParam很熟悉,而本章咱們自定義的註解跟@RequestParam相似,主要目的也是標識指定參數完成數據的綁定。下面咱們先來看看該註解的源碼,以下所示:

package com.yuqiyu.chapter36.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 參數實體映射註解
 * 配置該註解的參數會使用 com.yuqiyu.chapter36.resovler.CustomerArgumentResolver類完成參數裝載
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/16
 * Time:22:19
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Target(value = ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterModel
{
}

該註解目前沒有添加任何一個屬性,這個也是能夠根據項目的需求已經業務邏輯進行相應添加的,好比@RequestParam內經常使用的屬性requireddefaultValue等屬性,因爲咱們本章內容不須要自定義註解內的屬性因此這裏就不添加了。

該註解的做用域是在參數上@Target(value = ElementType.PARAMETER),咱們僅能夠在方法參數上使用。

建立參數接受實體

咱們能夠回到上面看看index.jsp的內容,咱們須要教師的基本信息以及學生的基本信息,那咱們就爲教師、以及學生建立實體(注意:這個實體能夠是對應數據庫內的實體)

教師實體
package com.yuqiyu.chapter36.bean;

import lombok.Data;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/17
 * Time:10:40
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
public class TeacherEntity {
    //教師姓名
    private String name;
}

教師實體內目前爲了測試就添加一個跟頁面參數有關的字段。

學生實體
package com.yuqiyu.chapter36.bean;

import lombok.Data;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/17
 * Time:10:41
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
public class StudentEntity {
    //學生姓名
    private String name;
    //年齡
    private String age;
}

學生實體添加與頁面參數對應的字段,名稱、年齡。

編寫CustomerArgumentResolver參數裝載

在寫參數裝載以前,咱們須要先了解下它的接口HandlerMethodArgumentResolver,該接口內定義了兩個方法:

supportsParameter
boolean supportsParameter(MethodParameter var1);

supportsParameter方法顧名思義,是容許裝載的參數,也就是說方法返回true時纔會指定裝載方法完成參數裝載。

resolveArgument
Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;

resolveArgument方法是參數狀態的實現邏輯方法,該方法返回的值會直接裝載到指定的參數上,有木有很神奇啊?下面咱們就建立實現類來揭開這位神奇的姑娘的面紗吧!

建立CustomerArgumentResolver實現接口HandlerMethodArgumentResolver內的兩個方法,具體實現代碼以下所示:

package com.yuqiyu.chapter36.resovler;

import com.yuqiyu.chapter36.annotation.ParameterModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 自定義參數裝載
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/16
 * Time:22:11
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
public class CustomerArgumentResolver
    implements HandlerMethodArgumentResolver
{
    /**
     * 日誌對象
     */
    private Logger logger = LoggerFactory.getLogger(CustomerArgumentResolver.class);
    /**
     * 該方法返回true時調用resolveArgument方法執行邏輯
     * spring家族的架構設計萬變不離其宗啊,在以前event & listener也是用到了一樣的方式
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ParameterModel.class);
    }

    /**
     * 裝載參數
     * @param methodParameter 方法參數
     * @param modelAndViewContainer 返回視圖容器
     * @param nativeWebRequest 本次請求對象
     * @param webDataBinderFactory 數據綁定工廠
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument (
            MethodParameter methodParameter,
            ModelAndViewContainer modelAndViewContainer,
            NativeWebRequest nativeWebRequest,
            WebDataBinderFactory webDataBinderFactory
    )
            throws Exception
    {
        String parameterName = methodParameter.getParameterName();
        logger.info("參數名稱:{}",parameterName);
        /**
         * 目標返回對象
         * 若是Model存在該Attribute時從module內獲取並設置爲返回值
         * 若是Model不存在該Attribute則從request parameterMap內獲取並設置爲返回值
         */
        Object target = modelAndViewContainer.containsAttribute(parameterName) ?
                modelAndViewContainer.getModel().get(parameterName) : createAttribute(parameterName, methodParameter, webDataBinderFactory, nativeWebRequest);;

        /**
         * 返回內容,這裏返回的內容纔是最終裝載到參數的值
         */
        return target;
    }

    /**
     * 根據參數attributeName獲取請求的值
     * @param attributeName 請求參數
     * @param parameter method 參數對象
     * @param binderFactory 數據綁定工廠
     * @param request 請求對象
     * @return
     * @throws Exception
     */
    protected Object createAttribute(String attributeName, MethodParameter parameter,
                                     WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        /**
         * 獲取attributeName的值
         */
        String value = getRequestValueForAttribute(attributeName, request);

        /**
         * 若是存在值
         */
        if (value != null) {
            /**
             * 進行類型轉換
             * 檢查請求的類型與目標參數類型是否能夠進行轉換
             */
            Object attribute = convertAttributeToParameterValue(value, attributeName, parameter, binderFactory, request);
            /**
             * 若是存在轉換後的值,則返回
             */
            if (attribute != null) {
                return attribute;
            }
        }
        /**
         * 檢查request parameterMap 內是否存在以attributeName做爲前綴的數據
         * 若是存在則根據字段的類型來進行設置值、集合、數組等
         */
        else
        {
            Object attribute = putParameters(parameter,request);
            if(attribute!=null)
            {
                return attribute;
            }
        }
        /**
         * 若是以上兩種條件不符合,直接返回初始化參數類型的空對象
         */
        return BeanUtils.instantiateClass(parameter.getParameterType());
    }

    /**
     * 將attribute的值轉換爲parameter參數值類型
     * @param sourceValue 源請求值
     * @param attributeName 參數名
     * @param parameter 目標參數對象
     * @param binderFactory 數據綁定工廠
     * @param request 請求對象
     * @return
     * @throws Exception
     */
    protected Object convertAttributeToParameterValue(String sourceValue,
                                                     String attributeName,
                                                     MethodParameter parameter,
                                                     WebDataBinderFactory binderFactory,
                                                     NativeWebRequest request) throws Exception {
        /**
         * 獲取類型轉換業務邏輯實現類
         */
        DataBinder binder = binderFactory.createBinder(request, null, attributeName);
        ConversionService conversionService = binder.getConversionService();
        if (conversionService != null) {
            /**
             * 源類型描述
             */
            TypeDescriptor source = TypeDescriptor.valueOf(String.class);
            /**
             * 根據目標參數對象獲取目標參數類型描述
             */
            TypeDescriptor target = new TypeDescriptor(parameter);
            /**
             * 驗證是否能夠進行轉換
             */
            if (conversionService.canConvert(source, target)) {
                /**
                 * 返回轉換後的值
                 */
                return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
            }
        }
        return null;
    }

    /**
     * 從request parameterMap集合內獲取attributeName的值
     * @param attributeName 參數名稱
     * @param request 請求對象
     * @return
     */
    protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
        /**
         * 獲取PathVariables參數集合
         */
        Map<String, String> variables = getUriTemplateVariables(request);
        /**
         * 若是PathVariables參數集合內存在該attributeName
         * 直接返回相對應的值
         */
        if (StringUtils.hasText(variables.get(attributeName))) {
            return variables.get(attributeName);
        }
        /**
         * 若是request parameterMap內存在該attributeName
         * 直接返回相對應的值
         */
        else if (StringUtils.hasText(request.getParameter(attributeName))) {
            return request.getParameter(attributeName);
        }
        //不存在時返回null
        else {
            return null;
        }
    }

    /**
     * 獲取指定前綴的參數:包括uri varaibles 和 parameters
     *
     * @param namePrefix
     * @param request
     * @return
     * @subPrefix 是否截取掉namePrefix的前綴
     */
    protected Map<String, String[]> getPrefixParameterMap(String namePrefix, NativeWebRequest request, boolean subPrefix) {
        Map<String, String[]> result = new HashMap();
        /**
         * 從PathVariables內獲取該前綴的參數列表
         */
        Map<String, String> variables = getUriTemplateVariables(request);

        int namePrefixLength = namePrefix.length();
        for (String name : variables.keySet()) {
            if (name.startsWith(namePrefix)) {

                //page.pn  則截取 pn
                if (subPrefix) {
                    char ch = name.charAt(namePrefix.length());
                    //若是下一個字符不是 數字 . _  則不多是查詢 只是前綴相似
                    if (illegalChar(ch)) {
                        continue;
                    }
                    result.put(name.substring(namePrefixLength + 1), new String[]{variables.get(name)});
                } else {
                    result.put(name, new String[]{variables.get(name)});
                }
            }
        }

        /**
         * 從request parameterMap集合內獲取該前綴的參數列表
         */
        Iterator<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasNext()) {
            String name = parameterNames.next();
            if (name.startsWith(namePrefix)) {
                //page.pn  則截取 pn
                if (subPrefix) {
                    char ch = name.charAt(namePrefix.length());
                    //若是下一個字符不是 數字 . _  則不多是查詢 只是前綴相似
                    if (illegalChar(ch)) {
                        continue;
                    }
                    result.put(name.substring(namePrefixLength + 1), request.getParameterValues(name));
                } else {
                    result.put(name, request.getParameterValues(name));
                }
            }
        }

        return result;
    }

    /**
     * 驗證參數前綴是否合法
     * @param ch
     * @return
     */
    private boolean illegalChar(char ch) {
        return ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9');
    }

    /**
     * 獲取PathVariables集合
     * @param request 請求對象
     * @return
     */
    protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
        Map<String, String> variables =
                (Map<String, String>) request.getAttribute(
                        HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        return (variables != null) ? variables : Collections.emptyMap();
    }

    /**
     * 從request內獲取parameter前綴的全部參數
     * 並根據parameter的類型將對應字段的值設置到parmaeter對象內並返回
     * @param parameter
     * @param request
     * @return
     */
    protected Object putParameters(MethodParameter parameter,NativeWebRequest request)
    {
        /**
         * 根據請求參數類型初始化空對象
         */
        Object object = BeanUtils.instantiateClass(parameter.getParameterType());
        /**
         * 獲取指定前綴的請求參數集合
         */
        Map<String, String[]> parameters = getPrefixParameterMap(parameter.getParameterName(),request,true);
        Iterator<String> iterator = parameters.keySet().iterator();
        while(iterator.hasNext())
        {
            //字段名稱
            String fieldName = iterator.next();
            //請求參數值
            String[] parameterValue = parameters.get(fieldName);
            try {
                Field field = object.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                //字段的類型
                Class<?> fieldTargetType = field.getType();

                /**
                 * List(ArrayList、LinkedList)類型
                 * 將數組類型的值轉換爲List集合對象
                 */
                if(List.class.isAssignableFrom(fieldTargetType))
                {
                    field.set(object, Arrays.asList(parameterValue));
                }
                /**
                 *Object數組類型,直接將數組值設置爲目標字段的值
                 */
                else if(Object[].class.isAssignableFrom(fieldTargetType))
                {
                    field.set(object, parameterValue);
                }
                /**
                 * 單值時獲取數組索引爲0的值
                 */
                else {
                    field.set(object, parameterValue[0]);
                }
            }
            catch (Exception e)
            {
                logger.error("Set Field:{} Value Error,In {}",fieldName,object.getClass().getName());
                continue;
            }
        }
        return object;
    }
}

上面我直接貼出了參數裝載的所有實現方法,下面咱們就開始按照裝載的流程進行講解。

supportsParameter方法實現
/**
     * 該方法返回true時調用resolveArgument方法執行邏輯
     * spring家族的架構設計萬變不離其宗啊,在以前event & listener也是用到了一樣的方式
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ParameterModel.class);
    }

咱們只對配置了ParameterModel註解的參數進行裝載。

resolveArgument方法實現
/**
     * 裝載參數
     * @param methodParameter 方法參數
     * @param modelAndViewContainer 返回視圖容器
     * @param nativeWebRequest 本次請求對象
     * @param webDataBinderFactory 數據綁定工廠
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument (
            MethodParameter methodParameter,
            ModelAndViewContainer modelAndViewContainer,
            NativeWebRequest nativeWebRequest,
            WebDataBinderFactory webDataBinderFactory
    )
            throws Exception
    {
        String parameterName = methodParameter.getParameterName();
        logger.info("參數名稱:{}",parameterName);
        /**
         * 目標返回對象
         * 若是Model存在該Attribute時從module內獲取並設置爲返回值
         * 若是Model不存在該Attribute則從request parameterMap內獲取並設置爲返回值
         */
        Object target = modelAndViewContainer.containsAttribute(parameterName) ?
                modelAndViewContainer.getModel().get(parameterName) : createAttribute(parameterName, methodParameter, webDataBinderFactory, nativeWebRequest);;

        /**
         * 返回內容,這裏返回的內容纔是最終裝載到參數的值
         */
        return target;
    }

該方法做爲裝載參數邏輯的入口,咱們從MethodParameter 對象內獲取了參數的名稱,根據該名稱檢查Model內是否存在該名稱的值,若是存在則直接使用並返回,反則須要從ParameterMap內獲取對應該參數名稱的值返回。
咱們下面主要看看從parameterMap獲取的方法實現

createAttribute方法實現
/**
     * 根據參數attributeName獲取請求的值
     * @param attributeName 請求參數
     * @param parameter method 參數對象
     * @param binderFactory 數據綁定工廠
     * @param request 請求對象
     * @return
     * @throws Exception
     */
    protected Object createAttribute(String attributeName, MethodParameter parameter,
                                     WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        /**
         * 獲取attributeName的值
         */
        String value = getRequestValueForAttribute(attributeName, request);

        /**
         * 若是存在值
         */
        if (value != null) {
            /**
             * 進行類型轉換
             * 檢查請求的類型與目標參數類型是否能夠進行轉換
             */
            Object attribute = convertAttributeToParameterValue(value, attributeName, parameter, binderFactory, request);
            /**
             * 若是存在轉換後的值,則返回
             */
            if (attribute != null) {
                return attribute;
            }
        }
        /**
         * 檢查request parameterMap 內是否存在以attributeName做爲前綴的數據
         * 若是存在則根據字段的類型來進行設置值、集合、數組等
         */
        else
        {
            Object attribute = putParameters(parameter,request);
            if(attribute!=null)
            {
                return attribute;
            }
        }
        /**
         * 若是以上兩種條件不符合,直接返回初始化參數類型的空對象
         */
        return BeanUtils.instantiateClass(parameter.getParameterType());
    }

該方法的邏輯存在兩個分支,首先經過調用getRequestValueForAttribute方法從parameterMap內獲取指定屬性名的請求值,若是存在值則須要驗證是否能夠完成類型轉換,驗證經過後則直接返回值。

上面的部分實際上是SpringMVC原有的參數裝載的流程,下面咱們就來根據需求個性化定製裝載邏輯。

putParameters方法實現

該方法實現了自定義規則xxx.xxx方式進行參數裝載的邏輯,咱們在前臺傳遞參數的時候只須要將Controller內方法參數名稱做爲傳遞的前綴便可,如:teacher.namestudent.name

/**
     * 從request內獲取parameter前綴的全部參數
     * 並根據parameter的類型將對應字段的值設置到parmaeter對象內並返回
     * @param parameter
     * @param request
     * @return
     */
    protected Object putParameters(MethodParameter parameter,NativeWebRequest request)
    {
        /**
         * 根據請求參數類型初始化空對象
         */
        Object object = BeanUtils.instantiateClass(parameter.getParameterType());
        /**
         * 獲取指定前綴的請求參數集合
         */
        Map<String, String[]> parameters = getPrefixParameterMap(parameter.getParameterName(),request,true);
        Iterator<String> iterator = parameters.keySet().iterator();
        while(iterator.hasNext())
        {
            //字段名稱
            String fieldName = iterator.next();
            //請求參數值
            String[] parameterValue = parameters.get(fieldName);
            try {
                Field field = object.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                //字段的類型
                Class<?> fieldTargetType = field.getType();

                /**
                 * List(ArrayList、LinkedList)類型
                 * 將數組類型的值轉換爲List集合對象
                 */
                if(List.class.isAssignableFrom(fieldTargetType))
                {
                    field.set(object, Arrays.asList(parameterValue));
                }
                /**
                 *Object數組類型,直接將數組值設置爲目標字段的值
                 */
                else if(Object[].class.isAssignableFrom(fieldTargetType))
                {
                    field.set(object, parameterValue);
                }
                /**
                 * 單值時獲取數組索引爲0的值
                 */
                else {
                    field.set(object, parameterValue[0]);
                }
            }
            catch (Exception e)
            {
                logger.error("Set Field:{} Value Error,In {}",fieldName,object.getClass().getName());
                continue;
            }
        }
        return object;
    }

該方法首先實例化了一個MethodParameter類型的空對象,而後經過getPrefixParameterMap獲取PathVariablesParameterMap內前綴爲MethodParameter名稱的請求參數列表,遍歷列表對應設置
object 內的字段,用於完成參數的裝載,在裝載過程當中,我這裏分別根據CollectionListArraySingle類型進行了處理(注意:這裏須要根據項目需求進行調整裝載類型)。

配置Spring託管CustomerArgumentResolver

咱們將CustomerArgumentResolver託管交付給Spring框架,咱們來建立一個名叫WebMvcConfiguration的配置類,該類繼承抽象類WebMvcConfigurerAdapter,代碼以下所示:

/**
 * springmvc 註解式配置類
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/16
 * Time:22:15
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration
public class WebMvcConfiguration
    extends WebMvcConfigurerAdapter
{
    /**
     * 添加參數裝載
     * @param argumentResolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        /**
         * 將自定義的參數裝載添加到spring內託管
         */
        argumentResolvers.add(new CustomerArgumentResolver());
    }

    /**
     * 配置靜態請求視圖映射
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("index");
    }
}

咱們重寫了WebMvcConfigurerAdapter抽象類內的兩個方法addArgumentResolversaddViewControllers,其中addArgumentResolvers方法完成了參數裝載的託管。

addViewControllers配置了視圖控制器映射,這樣咱們訪問/index地址就能夠請求到index.jsp頁面。

建立測試控制器

建立名爲IndexController的控制器並添加數據提交的方法,具體代碼以下所示:

/**
 * 表單提交控制器
 * ========================
 * Created with IntelliJ IDEA.
 * User:恆宇少年
 * Date:2017/9/16
 * Time:22:26
 * 碼雲:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class IndexController
{
    /**
     * 裝載參數測試
     * @return
     */
    @RequestMapping(value = "/submit")
    public String resolver(@ParameterModel TeacherEntity teacher, @ParameterModel StudentEntity student)
    {
        return "教師名稱:"+ JSON.toJSON(teacher.getName()) +",學生名稱:"+student.getName()+",學生年齡:"+student.getAge();
    }
}

能夠看到咱們爲TeacherEntity StudentEntity 分別添加了註解@ParameterModel,也就證實了這兩個實體須要使用咱們的CustomerArgumentResolver完成參數裝載。

運行測試

在運行測試以前,咱們須要修改下index.jsp內的參數映射前綴,修改後代碼以下所示:

<form method="post" action="/submit">
        教師姓名:<input type="text" name="teacher.name"/><br/><br/>
        學生姓名:<input type="text" name="student.name"/><br/><br/>
        學生年齡:<input type="text" name="student.age"/><br/><br/>
        <input type="submit"/>
    </form>

測試單值裝載

咱們爲教師名稱、學生名稱、學生年齡都分別添加了前綴,下面咱們來啓動項目,訪問項目根下路徑/index,以下圖1所示:

圖1
在上圖1中輸入了部分請求參數,點擊「提交」按鈕查看界面輸出的效果,圖下所示:

教師名稱:王老師,學生名稱:張小跑,學生年齡:23

能夠看到參數已經被正確的裝載到了不一樣的實體類內。

上面的例子只是針對實體內的單個值的裝載,下面咱們來測試下List類型的值是否能夠裝載?

測試List裝載

咱們先來修改下教師實體內的名稱爲List,字段名稱不須要變更,以下所示:

//教師姓名
private List<String> name;

再來修改下index.jsp輸入框,以下所示:

<form method="post" action="/submit">
        語文老師姓名:<input type="text" name="teacher.name"/><br/><br/>
        數學教師姓名:<input type="text" name="teacher.name"/><br/><br/>
        學生姓名:<input type="text" name="student.name"/><br/><br/>
        學生年齡:<input type="text" name="student.age"/><br/><br/>
        <input type="submit"/>
    </form>

在上代碼中咱們添加了兩位老師的名稱,接下來重啓項目,再次提交測試,查看是否是咱們想要的效果?
修改後的界面以下圖2所示:

圖2

界面輸出內容以下所示:

教師名稱:["王老師","李老師"],學生名稱:張小跑,學生年齡:24

能夠看到咱們已經拿到了兩位老師的名稱,這也證實了咱們的CustomerArgumentResolver是能夠完成List的映射裝載的。

總結

以上內容就是本章的所有講解內容,本章簡單實現了參數的狀態,其中還有不少細節性質的邏輯,如:@Valid註解的生效、文件的上傳等。在下一章咱們會降到若是經過參數裝載實現接口服務的安全認證。

本章代碼已經上傳到碼雲:
網頁地址:http://git.oschina.net/jnyqy/lessons
Git地址:https://git.oschina.net/jnyqy/lessons.git
SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄
感謝閱讀!
歡迎加入QQ技術交流羣,共同進步。
QQ技術交流羣

相關文章
相關標籤/搜索