摘要:本章目標根據(jù)項目定制參數(shù)狀態(tài)并了解的裝載過程以及實現(xiàn)方式。創(chuàng)建測試控制器創(chuàng)建名為的控制器并添加數(shù)據(jù)提交的方法,具體代碼如下所示表單提交控制器恒宇少年碼云裝載參數(shù)測試教師名稱,
在國內(nèi)企業(yè)開發(fā)項目中大多數(shù)都已經(jīng)偏向Spring家族式的開發(fā)風格,在前幾年國內(nèi)項目都是以Structs2作為Web開發(fā)的主導,不過由于近幾年發(fā)生的事情確實讓開發(fā)者對它失去了以往的信心。與此同時Spring家族發(fā)布了SpringMVC,而且完美的整合Spring來開發(fā)企業(yè)級大型Web項目。它有著比Structs2更強大的技術支持以及更靈活的自定義配置,接下來我們就看看本章的內(nèi)容,我們自定義實現(xiàn)SpringMVC參數(shù)綁定規(guī)則,根據(jù)業(yè)務定制參數(shù)裝載實現(xiàn)方式。
本章目標根據(jù)項目定制SpringMVC參數(shù)狀態(tài)并了解SpringMVC的裝載過程以及實現(xiàn)方式。
構建項目我們先來創(chuàng)建一個SpringBoot項目,添加本章所需的依賴,pom.xml配置文件如下所示:
...//省略部分配置...//省略部分配置 org.springframework.boot spring-boot-starter-web org.apache.tomcat.embed tomcat-embed-jasper javax.servlet javax.servlet-api javax.servlet jstl org.projectlombok lombok com.alibaba fastjson 1.2.38 org.springframework.boot spring-boot-starter-test test
本章需要JSP相關的依賴支持,所以需要添加對應的依賴,修改application.properties配置文件讓JSP生效,配置內(nèi)容如下所示:
spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp
相關JSP配置可以訪問第二章:SpringBoot與JSP間不可描述的秘密查看講解。
SpringMVC的參數(shù)裝載在講解我們自定義參數(shù)裝載之前,我們先來看看SpringMVC內(nèi)部為我們提供的參數(shù)裝載方式。
我們首先來添加一個測試的jsp頁面,頁面上添加一些輸入元素,代碼如下所示:
<%-- 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" %>Title
在index.jsp內(nèi)添加了三個name的文本輸入框,如果我們現(xiàn)在提交到后臺SpringMVC為默認為我們解析成一個數(shù)組,如果根據(jù)描述而言的來處理則是不合理的,當然也可以使用各種手段完成字段參數(shù)的裝載,比如:為教師的name添加一個數(shù)組或者List集合進行接受,這種方式也是可以實現(xiàn)但不優(yōu)雅。
如果你們項目組有嚴格的開發(fā)規(guī)范要求,這種方式是不允許出現(xiàn)在Controller方法內(nèi)的。
那這個問題就讓人頭疼了,在之前我們使用Struct2的時候是可以根據(jù)指定的前綴,如:xxx.xxx來進行映射的,而SpringMVC并沒有提供這個支持,不過它提供了自定義參數(shù)裝載的實現(xiàn)方法,那就沒有問題了,我們可以手寫。
自定義的參數(shù)裝載既然上面的代碼實現(xiàn)滿足不了我們的需求,那么我接下來就來重寫參數(shù)裝載。
對于一直使用SpringMVC的朋友來說,應該對@RequestParam很熟悉,而本章我們自定義的注解跟@RequestParam類似,主要目的也是標識指定參數(shù)完成數(shù)據(jù)的綁定。下面我們先來看看該注解的源碼,如下所示:
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; /** * 參數(shù)實體映射注解 * 配置該注解的參數(shù)會使用 com.yuqiyu.chapter36.resovler.CustomerArgumentResolver類完成參數(shù)裝載 * ======================== * 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 { }
該注解目前沒有添加任何一個屬性,這個也是可以根據(jù)項目的需求已經(jīng)業(yè)務邏輯進行相應添加的,比如@RequestParam內(nèi)常用的屬性required、defaultValue等屬性,由于我們本章內(nèi)容不需要自定義注解內(nèi)的屬性所以這里就不添加了。
該注解的作用域是在參數(shù)上@Target(value = ElementType.PARAMETER),我們僅可以在方法參數(shù)上使用。
創(chuàng)建參數(shù)接受實體我們可以回到上面看看index.jsp的內(nèi)容,我們需要教師的基本信息以及學生的基本信息,那我們就為教師、以及學生創(chuàng)建實體(注意:這個實體可以是對應數(shù)據(jù)庫內(nèi)的實體)
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; }
教師實體內(nèi)目前為了測試就添加一個跟頁面參數(shù)有關的字段。
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; }
學生實體添加與頁面參數(shù)對應的字段,名稱、年齡。
編寫CustomerArgumentResolver參數(shù)裝載在寫參數(shù)裝載之前,我們需要先了解下它的接口HandlerMethodArgumentResolver,該接口內(nèi)定義了兩個方法:
boolean supportsParameter(MethodParameter var1);
supportsParameter方法顧名思義,是允許裝載的參數(shù),也就是說方法返回true時才會指定裝載方法完成參數(shù)裝載。
Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
resolveArgument方法是參數(shù)狀態(tài)的實現(xiàn)邏輯方法,該方法返回的值會直接裝載到指定的參數(shù)上,有木有很神奇啊?下面我們就創(chuàng)建實現(xiàn)類來揭開這位神奇的姑娘的面紗吧!
創(chuàng)建CustomerArgumentResolver實現(xiàn)接口HandlerMethodArgumentResolver內(nèi)的兩個方法,具體實現(xiàn)代碼如下所示:
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.*; /** * 自定義參數(shù)裝載 * ======================== * 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時調(diào)用resolveArgument方法執(zhí)行邏輯 * spring家族的架構設計萬變不離其宗啊,在之前event & listener也是用到了同樣的方式 * @param methodParameter * @return */ @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(ParameterModel.class); } /** * 裝載參數(shù) * @param methodParameter 方法參數(shù) * @param modelAndViewContainer 返回視圖容器 * @param nativeWebRequest 本次請求對象 * @param webDataBinderFactory 數(shù)據(jù)綁定工廠 * @return * @throws Exception */ @Override public Object resolveArgument ( MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory ) throws Exception { String parameterName = methodParameter.getParameterName(); logger.info("參數(shù)名稱:{}",parameterName); /** * 目標返回對象 * 如果Model存在該Attribute時從module內(nèi)獲取并設置為返回值 * 如果Model不存在該Attribute則從request parameterMap內(nèi)獲取并設置為返回值 */ Object target = modelAndViewContainer.containsAttribute(parameterName) ? modelAndViewContainer.getModel().get(parameterName) : createAttribute(parameterName, methodParameter, webDataBinderFactory, nativeWebRequest);; /** * 返回內(nèi)容,這里返回的內(nèi)容才是最終裝載到參數(shù)的值 */ return target; } /** * 根據(jù)參數(shù)attributeName獲取請求的值 * @param attributeName 請求參數(shù) * @param parameter method 參數(shù)對象 * @param binderFactory 數(shù)據(jù)綁定工廠 * @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) { /** * 進行類型轉換 * 檢查請求的類型與目標參數(shù)類型是否可以進行轉換 */ Object attribute = convertAttributeToParameterValue(value, attributeName, parameter, binderFactory, request); /** * 如果存在轉換后的值,則返回 */ if (attribute != null) { return attribute; } } /** * 檢查request parameterMap 內(nèi)是否存在以attributeName作為前綴的數(shù)據(jù) * 如果存在則根據(jù)字段的類型來進行設置值、集合、數(shù)組等 */ else { Object attribute = putParameters(parameter,request); if(attribute!=null) { return attribute; } } /** * 如果以上兩種條件不符合,直接返回初始化參數(shù)類型的空對象 */ return BeanUtils.instantiateClass(parameter.getParameterType()); } /** * 將attribute的值轉換為parameter參數(shù)值類型 * @param sourceValue 源請求值 * @param attributeName 參數(shù)名 * @param parameter 目標參數(shù)對象 * @param binderFactory 數(shù)據(jù)綁定工廠 * @param request 請求對象 * @return * @throws Exception */ protected Object convertAttributeToParameterValue(String sourceValue, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { /** * 獲取類型轉換業(yè)務邏輯實現(xiàn)類 */ DataBinder binder = binderFactory.createBinder(request, null, attributeName); ConversionService conversionService = binder.getConversionService(); if (conversionService != null) { /** * 源類型描述 */ TypeDescriptor source = TypeDescriptor.valueOf(String.class); /** * 根據(jù)目標參數(shù)對象獲取目標參數(shù)類型描述 */ TypeDescriptor target = new TypeDescriptor(parameter); /** * 驗證是否可以進行轉換 */ if (conversionService.canConvert(source, target)) { /** * 返回轉換后的值 */ return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter); } } return null; } /** * 從request parameterMap集合內(nèi)獲取attributeName的值 * @param attributeName 參數(shù)名稱 * @param request 請求對象 * @return */ protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) { /** * 獲取PathVariables參數(shù)集合 */ Mapvariables = getUriTemplateVariables(request); /** * 如果PathVariables參數(shù)集合內(nèi)存在該attributeName * 直接返回相對應的值 */ if (StringUtils.hasText(variables.get(attributeName))) { return variables.get(attributeName); } /** * 如果request parameterMap內(nèi)存在該attributeName * 直接返回相對應的值 */ else if (StringUtils.hasText(request.getParameter(attributeName))) { return request.getParameter(attributeName); } //不存在時返回null else { return null; } } /** * 獲取指定前綴的參數(shù):包括uri varaibles 和 parameters * * @param namePrefix * @param request * @return * @subPrefix 是否截取掉namePrefix的前綴 */ protected Map getPrefixParameterMap(String namePrefix, NativeWebRequest request, boolean subPrefix) { Map result = new HashMap(); /** * 從PathVariables內(nèi)獲取該前綴的參數(shù)列表 */ Map 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()); //如果下一個字符不是 數(shù)字 . _ 則不可能是查詢 只是前綴類似 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集合內(nèi)獲取該前綴的參數(shù)列表 */ Iterator 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()); //如果下一個字符不是 數(shù)字 . _ 則不可能是查詢 只是前綴類似 if (illegalChar(ch)) { continue; } result.put(name.substring(namePrefixLength + 1), request.getParameterValues(name)); } else { result.put(name, request.getParameterValues(name)); } } } return result; } /** * 驗證參數(shù)前綴是否合法 * @param ch * @return */ private boolean illegalChar(char ch) { return ch != "." && ch != "_" && !(ch >= "0" && ch <= "9"); } /** * 獲取PathVariables集合 * @param request 請求對象 * @return */ protected final Map getUriTemplateVariables(NativeWebRequest request) { Map variables = (Map ) request.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); return (variables != null) ? variables : Collections.emptyMap(); } /** * 從request內(nèi)獲取parameter前綴的所有參數(shù) * 并根據(jù)parameter的類型將對應字段的值設置到parmaeter對象內(nèi)并返回 * @param parameter * @param request * @return */ protected Object putParameters(MethodParameter parameter,NativeWebRequest request) { /** * 根據(jù)請求參數(shù)類型初始化空對象 */ Object object = BeanUtils.instantiateClass(parameter.getParameterType()); /** * 獲取指定前綴的請求參數(shù)集合 */ Map parameters = getPrefixParameterMap(parameter.getParameterName(),request,true); Iterator iterator = parameters.keySet().iterator(); while(iterator.hasNext()) { //字段名稱 String fieldName = iterator.next(); //請求參數(shù)值 String[] parameterValue = parameters.get(fieldName); try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); //字段的類型 Class> fieldTargetType = field.getType(); /** * List(ArrayList、LinkedList)類型 * 將數(shù)組類型的值轉換為List集合對象 */ if(List.class.isAssignableFrom(fieldTargetType)) { field.set(object, Arrays.asList(parameterValue)); } /** *Object數(shù)組類型,直接將數(shù)組值設置為目標字段的值 */ else if(Object[].class.isAssignableFrom(fieldTargetType)) { field.set(object, parameterValue); } /** * 單值時獲取數(shù)組索引為0的值 */ else { field.set(object, parameterValue[0]); } } catch (Exception e) { logger.error("Set Field:{} Value Error,In {}",fieldName,object.getClass().getName()); continue; } } return object; } }
上面我直接貼出了參數(shù)裝載的全部實現(xiàn)方法,下面我們就開始按照裝載的流程進行講解。
/** * 該方法返回true時調(diào)用resolveArgument方法執(zhí)行邏輯 * spring家族的架構設計萬變不離其宗啊,在之前event & listener也是用到了同樣的方式 * @param methodParameter * @return */ @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(ParameterModel.class); }
我們只對配置了ParameterModel注解的參數(shù)進行裝載。
/** * 裝載參數(shù) * @param methodParameter 方法參數(shù) * @param modelAndViewContainer 返回視圖容器 * @param nativeWebRequest 本次請求對象 * @param webDataBinderFactory 數(shù)據(jù)綁定工廠 * @return * @throws Exception */ @Override public Object resolveArgument ( MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory ) throws Exception { String parameterName = methodParameter.getParameterName(); logger.info("參數(shù)名稱:{}",parameterName); /** * 目標返回對象 * 如果Model存在該Attribute時從module內(nèi)獲取并設置為返回值 * 如果Model不存在該Attribute則從request parameterMap內(nèi)獲取并設置為返回值 */ Object target = modelAndViewContainer.containsAttribute(parameterName) ? modelAndViewContainer.getModel().get(parameterName) : createAttribute(parameterName, methodParameter, webDataBinderFactory, nativeWebRequest);; /** * 返回內(nèi)容,這里返回的內(nèi)容才是最終裝載到參數(shù)的值 */ return target; }
該方法作為裝載參數(shù)邏輯的入口,我們從MethodParameter 對象內(nèi)獲取了參數(shù)的名稱,根據(jù)該名稱檢查Model內(nèi)是否存在該名稱的值,如果存在則直接使用并返回,反則需要從ParameterMap內(nèi)獲取對應該參數(shù)名稱的值返回。
我們下面主要看看從parameterMap獲取的方法實現(xiàn)
/** * 根據(jù)參數(shù)attributeName獲取請求的值 * @param attributeName 請求參數(shù) * @param parameter method 參數(shù)對象 * @param binderFactory 數(shù)據(jù)綁定工廠 * @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) { /** * 進行類型轉換 * 檢查請求的類型與目標參數(shù)類型是否可以進行轉換 */ Object attribute = convertAttributeToParameterValue(value, attributeName, parameter, binderFactory, request); /** * 如果存在轉換后的值,則返回 */ if (attribute != null) { return attribute; } } /** * 檢查request parameterMap 內(nèi)是否存在以attributeName作為前綴的數(shù)據(jù) * 如果存在則根據(jù)字段的類型來進行設置值、集合、數(shù)組等 */ else { Object attribute = putParameters(parameter,request); if(attribute!=null) { return attribute; } } /** * 如果以上兩種條件不符合,直接返回初始化參數(shù)類型的空對象 */ return BeanUtils.instantiateClass(parameter.getParameterType()); }
該方法的邏輯存在兩個分支,首先通過調(diào)用getRequestValueForAttribute方法從parameterMap內(nèi)獲取指定屬性名的請求值,如果存在值則需要驗證是否可以完成類型轉換,驗證通過后則直接返回值。
上面的部分其實是SpringMVC原有的參數(shù)裝載的流程,下面我們就來根據(jù)需求個性化定制裝載邏輯。
該方法實現(xiàn)了自定義規(guī)則xxx.xxx方式進行參數(shù)裝載的邏輯,我們在前臺傳遞參數(shù)的時候只需要將Controller內(nèi)方法參數(shù)名稱作為傳遞的前綴即可,如:teacher.name、student.name。
/** * 從request內(nèi)獲取parameter前綴的所有參數(shù) * 并根據(jù)parameter的類型將對應字段的值設置到parmaeter對象內(nèi)并返回 * @param parameter * @param request * @return */ protected Object putParameters(MethodParameter parameter,NativeWebRequest request) { /** * 根據(jù)請求參數(shù)類型初始化空對象 */ Object object = BeanUtils.instantiateClass(parameter.getParameterType()); /** * 獲取指定前綴的請求參數(shù)集合 */ Mapparameters = getPrefixParameterMap(parameter.getParameterName(),request,true); Iterator iterator = parameters.keySet().iterator(); while(iterator.hasNext()) { //字段名稱 String fieldName = iterator.next(); //請求參數(shù)值 String[] parameterValue = parameters.get(fieldName); try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); //字段的類型 Class> fieldTargetType = field.getType(); /** * List(ArrayList、LinkedList)類型 * 將數(shù)組類型的值轉換為List集合對象 */ if(List.class.isAssignableFrom(fieldTargetType)) { field.set(object, Arrays.asList(parameterValue)); } /** *Object數(shù)組類型,直接將數(shù)組值設置為目標字段的值 */ else if(Object[].class.isAssignableFrom(fieldTargetType)) { field.set(object, parameterValue); } /** * 單值時獲取數(shù)組索引為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獲取PathVariables、ParameterMap內(nèi)前綴為MethodParameter名稱的請求參數(shù)列表,遍歷列表對應設置
object 內(nèi)的字段,用于完成參數(shù)的裝載,在裝載過程中,我這里分別根據(jù)Collection、List、Array、Single類型進行了處理(注意:這里需要根據(jù)項目需求進行調(diào)整裝載類型)。
我們將CustomerArgumentResolver托管交付給Spring框架,我們來創(chuàng)建一個名叫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 { /** * 添加參數(shù)裝載 * @param argumentResolvers */ @Override public void addArgumentResolvers(ListargumentResolvers) { /** * 將自定義的參數(shù)裝載添加到spring內(nèi)托管 */ argumentResolvers.add(new CustomerArgumentResolver()); } /** * 配置靜態(tài)請求視圖映射 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/index").setViewName("index"); } }
我們重寫了WebMvcConfigurerAdapter抽象類內(nèi)的兩個方法addArgumentResolvers、addViewControllers,其中addArgumentResolvers方法完成了參數(shù)裝載的托管。
addViewControllers配置了視圖控制器映射,這樣我們訪問/index地址就可以請求到index.jsp頁面。
創(chuàng)建測試控制器創(chuàng)建名為IndexController的控制器并添加數(shù)據(jù)提交的方法,具體代碼如下所示:
/** * 表單提交控制器 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/9/16 * Time:22:26 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @RestController public class IndexController { /** * 裝載參數(shù)測試 * @return */ @RequestMapping(value = "/submit") public String resolver(@ParameterModel TeacherEntity teacher, @ParameterModel StudentEntity student) { return "教師名稱:"+ JSON.toJSON(teacher.getName()) +",學生名稱:"+student.getName()+",學生年齡:"+student.getAge(); } }
可以看到我們?yōu)?b>TeacherEntity 、StudentEntity 分別添加了注解@ParameterModel,也就證明了這兩個實體需要使用我們的CustomerArgumentResolver完成參數(shù)裝載。
運行測試在運行測試之前,我們需要修改下index.jsp內(nèi)的參數(shù)映射前綴,修改后代碼如下所示:
測試單值裝載我們?yōu)榻處熋Q、學生名稱、學生年齡都分別添加了前綴,下面我們來啟動項目,訪問項目根下路徑/index,如下圖1所示:
在上圖1中輸入了部分請求參數(shù),點擊“提交”按鈕查看界面輸出的效果,圖下所示:
教師名稱:王老師,學生名稱:張小跑,學生年齡:23
可以看到參數(shù)已經(jīng)被正確的裝載到了不同的實體類內(nèi)。
測試List裝載上面的例子只是針對實體內(nèi)的單個值的裝載,下面我們來測試下List類型的值是否可以裝載?
我們先來修改下教師實體內(nèi)的名稱為List,字段名稱不需要變動,如下所示:
//教師姓名 private Listname;
再來修改下index.jsp輸入框,如下所示:
在上代碼中我們添加了兩位老師的名稱,接下來重啟項目,再次提交測試,查看是不是我們想要的效果?
修改后的界面如下圖2所示:
界面輸出內(nèi)容如下所示:
教師名稱:["王老師","李老師"],學生名稱:張小跑,學生年齡:24
可以看到我們已經(jīng)拿到了兩位老師的名稱,這也證明了我們的CustomerArgumentResolver是可以完成List的映射裝載的。
總結以上內(nèi)容就是本章的全部講解內(nèi)容,本章簡單實現(xiàn)了參數(shù)的狀態(tài),其中還有很多細節(jié)性質的邏輯,如:@Valid注解的生效、文件的上傳等。在下一章我們會降到如果通過參數(shù)裝載實現(xiàn)接口服務的安全認證。
本章代碼已經(jīng)上傳到碼云:
網(wǎng)頁地址:http://git.oschina.net/jnyqy/lessons
Git地址:https://git.oschina.net/jnyqy/lessons.git
SpringBoot相關系列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關系列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關系列文章請訪問:目錄:SpringDataJPA學習目錄
感謝閱讀!
歡迎加入QQ技術交流群,共同進步。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67618.html
摘要:問題來了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動類測試啟動項目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對的描述,對于而言,這個必定是。的核心在的方法中。 之前已經(jīng)分析過了Spring的IOC(《零基礎帶你看Spring源碼——IOC控制反轉》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次...
摘要:本章目的基于平臺整合分別完成客戶端服務端的單元測試。在測試控制器內(nèi)添加了三個測試方法,我們接下來開始編寫單元測試代碼。總結本章主要介紹了基于平臺的兩種單元測試方式,一種是在服務端采用注入方式將需要測試的或者注入到測試類中,然后調(diào)用方法即可。 單元測試對于開發(fā)人員來說是非常熟悉的,我們每天的工作也都是圍繞著開發(fā)與測試進行的,在最早的時候測試都是采用工具Debug模式進行調(diào)試程序,后來Ju...
摘要:對象是開源框架的上下文對象實例,在項目運行時自動裝載內(nèi)的所有信息到內(nèi)存。總結本章內(nèi)容較少,主要講解了平臺下采用的方式完成實例的獲取,并通過實例完成對管理的實例手動獲取。 ApplicationContext對象是Spring開源框架的上下文對象實例,在項目運行時自動裝載Handler內(nèi)的所有信息到內(nèi)存。傳統(tǒng)的獲取方式有很多種,不過隨著Spring版本的不斷迭代,官方也慢慢的不建議使用部...
摘要:本章目標繼承采用形式實現(xiàn)個性化配置定制。本章代碼已經(jīng)上傳到碼云網(wǎng)頁地址地址相關系列文章請訪問目錄學習目錄相關系列文章請訪問通用查詢框架學習目錄相關系列文章請訪問目錄學習目錄感謝閱讀歡迎加入技術交流群,共同進步。 WebMvcConfigurerAdapter配置類其實是Spring內(nèi)部的一種配置方式,采用JavaBean的形式來代替?zhèn)鹘y(tǒng)的xml配置文件形式進行針對框架個性化定制,下面我...
摘要:小時學會學習總結時間年月日星期六說明本文部分內(nèi)容均來自慕課網(wǎng)。慕課網(wǎng)教學示例源碼暫無。數(shù)據(jù)庫操作下第六章事務管理事務管理只有查詢的時候不加事務,其它任何操作都要加事務。第七章課程回顧課程回顧總結介紹安裝配置的使用數(shù)據(jù)庫操作 《2小時學會SpringBoot》學習總結 時間:2017年2月18日星期六說明:本文部分內(nèi)容均來自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學示...
閱讀 1827·2021-11-11 16:55
閱讀 1452·2019-08-30 15:54
閱讀 769·2019-08-29 15:34
閱讀 2253·2019-08-29 13:11
閱讀 2908·2019-08-26 13:28
閱讀 1878·2019-08-26 10:49
閱讀 992·2019-08-26 10:40
閱讀 2553·2019-08-23 18:21