在国内企业开发项目中大多数都已经偏向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
内部为咱们提供的参数装载方式。数据库
咱们首先来添加一个测试的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
并无提供这个支持,不过它提供了自定义参数装载的实现方法,那就没有问题了,咱们能够手写。数组
既然上面的代码实现知足不了咱们的需求,那么我接下来就来重写参数装载。
对于一直使用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
内经常使用的属性required
、defaultValue
等属性,因为咱们本章内容不须要自定义注解内的属性因此这里就不添加了。
该注解的做用域是在参数上@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; }
学生实体添加与页面参数对应的字段,名称、年龄。
在写参数装载以前,咱们须要先了解下它的接口HandlerMethodArgumentResolver
,该接口内定义了两个方法:
boolean supportsParameter(MethodParameter var1);
supportsParameter
方法顾名思义,是容许装载的参数,也就是说方法返回true
时才会指定装载方法完成参数装载。
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; } }
上面我直接贴出了参数装载的所有实现方法,下面咱们就开始按照装载的流程进行讲解。
/** * 该方法返回true时调用resolveArgument方法执行逻辑 * spring家族的架构设计万变不离其宗啊,在以前event & listener也是用到了一样的方式 * @param methodParameter * @return */ @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(ParameterModel.class); }
咱们只对配置了ParameterModel
注解的参数进行装载。
/** * 装载参数 * @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
获取的方法实现
/** * 根据参数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
原有的参数装载的流程,下面咱们就来根据需求个性化定制装载逻辑。
该方法实现了自定义规则xxx.xxx
方式进行参数装载的逻辑,咱们在前台传递参数的时候只须要将Controller
内方法参数名称做为传递的前缀便可,如:teacher.name
、student.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
获取PathVariables
、ParameterMap
内前缀为MethodParameter
名称的请求参数列表,遍历列表对应设置object
内的字段,用于完成参数的装载,在装载过程当中,我这里分别根据Collection
、List
、Array
、Single
类型进行了处理(注意:这里须要根据项目需求进行调整装载类型)。
咱们将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
抽象类内的两个方法addArgumentResolvers
、addViewControllers
,其中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中输入了部分请求参数,点击“提交”按钮查看界面输出的效果,图下所示:
教师名称:王老师,学生名称:张小跑,学生年龄:23
能够看到参数已经被正确的装载到了不一样的实体类内。
上面的例子只是针对实体内的单个值的装载,下面咱们来测试下
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所示:
界面输出内容以下所示:
教师名称:["王老师","李老师"],学生名称:张小跑,学生年龄: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技术交流群,共同进步。