Spring 之请求参数解析原理(实体类传参解析)
之前实习的时候有做一个需要反射执行类中方法的组件,对该方法支持的参数类型的映射与解析当时考虑了很多参数类型, 参数类型有 普通类型 数组 实体类 泛型 等更多组合的复杂类型,对于反射执行的方法若重载方法很多,其实是需要确定参数类型来找到对应执行的方法
那么在Spring项目中,对于传参,其实也是可以直接以实体类的形式传参,就很好奇,其是怎么做的?所以看了看源码,分析记录一下
关于Spring的核心元素 DispatchServlet 请查看:Spring 原理之 DispatchServlet 原理
前言
在定义一个接口时,有很多种方式来实现接口的参数接收,常用的有以下三种:
-
request 作为接口的方法参数,然后request 根据 key获取传递的参数值
@GetMapping("/request_getValue") public ResponseEntity<User> requestGetValue(HttpServletRequest request){ // request 根据 key 获取值 String username = request.getParameter("username"); String name = request.getParameter("name"); User user = new User(); user.setName(name); user.setUsername(username); return new ResponseEntity<>(user , HttpStatus.OK); }
-
直接以方法参数的方式进接收传递的参数值
// 直接以参数的形式获取参数值 @GetMapping("/param_getValue") public ResponseEntity<User> paramGetValue(String username, String name){ User user = new User(); user.setName(name); user.setUsername(username); return new ResponseEntity<>(user , HttpStatus.OK); }
-
利用 Pojo 类 以方法参数的方式来封装获取参数值
@GetMapping("/pojo_getValue") public ResponseEntity<User> pojoGetValue(User user){ return new ResponseEntity<>(user , HttpStatus.OK); }
那么针对上述的第三种方式,底层是如何进行封装实现的呢?下面来跟踪一下源码,看看具体如何实现的~
原理解析
定位参数处理
关于Spring,不得不提其前端控制器 DispatcherServlet ,所有的请求,都是尤其进行处理的,即调用 DispatcherServlet 中的 **doDispatch() **方法进行处理
流程图如下:(具体的查看 Spring 原理之 DispatchServlet 原理)
根据上述流程调试过程经过以下部分源码:
- 1)org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//...
if (!mappedHandler.applyPreHandle(processedRequest, response)) return;
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//...
mappedHandler.applyPostHandle(processedRequest, response, mv);
//...
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
- 2)org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
- 3)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//...
mav = invokeHandlerMethod(request, response, handlerMethod);
//...
return mav;
}
- 4)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//...
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//...
invocableMethod.setDataBinderFactory(binderFactory);
//...
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//...
// 核心方法 invokeAndHandle
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
//...
}
说明:
- RequestMappingHandlerAdapter 已经获取到了 argumentResolvers 列表,用于处理接口参数的处理,后续流程会根据参数类型 找到合适的resolver 进行解析
WebDataBinderFactory 用来后续创建 WebDataBinder(数据绑定器,进行数据绑定的工作)
其中比较重要的有三个东西(validators、conversionService 、 converters )
1)**validators:**数据校验器,负责 **数据校验 ** 功能,一般用于对使用@Validated 注解参数的校验
2)conversionService:负责数据类型的转换和格式化工作
3)converters :负责各种 数据类型的转换 工作 ,可查看Spring 请求参数类型转换解析(@DateTimeFormat 、自定义Convert)
- 5)org.springframework.web.servlet.mvc.method.annotation#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 核心方法,处理request请求
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
//.....
mavContainer.setRequestHandled(false);
//.....
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
//.....
}
-
6)org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
// invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 获取参数信息进行解析
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
return this.doInvoke(args);
}
// getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 获取到方法的请求参数数组
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
// 逐一处理每个参数
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//.....
// 找到支持该类型的 resolver(从 argumentResolvers 列表中的resolver一个一个判断)
// 如果没找到即报错
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
//.....
//真正的解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
return args;
}
上述六个步骤的总结:
- 首先,对于请求的每一个参数,会逐一进行处理,找到合适的 resolver 来进行处理,默认的resolver有以下26个
- 0 = {RequestParamMethodArgumentResolver@7782}
1 = {RequestParamMapMethodArgumentResolver@7783}
2 = {PathVariableMethodArgumentResolver@7784}
3 = {PathVariableMapMethodArgumentResolver@7785}
4 = {MatrixVariableMethodArgumentResolver@7786}
5 = {MatrixVariableMapMethodArgumentResolver@7787}
6 = {ServletModelAttributeMethodProcessor@7788}
7 = {RequestResponseBodyMethodProcessor@7789}
8 = {RequestPartMethodArgumentResolver@7790}
9 = {RequestHeaderMethodArgumentResolver@7791}
10 = {RequestHeaderMapMethodArgumentResolver@7792}
11 = {ServletCookieValueMethodArgumentResolver@7793}
12 = {ExpressionValueMethodArgumentResolver@7794}
13 = {SessionAttributeMethodArgumentResolver@7795}
14 = {RequestAttributeMethodArgumentResolver@7796}
15 = {ServletRequestMethodArgumentResolver@7797}
16 = {ServletResponseMethodArgumentResolver@7798}
17 = {HttpEntityMethodProcessor@7799}
18 = {RedirectAttributesMethodArgumentResolver@7800}
19 = {ModelMethodProcessor@7801}
20 = {MapMethodProcessor@7802}
21 = {ErrorsMethodArgumentResolver@7803}
22 = {SessionStatusMethodArgumentResolver@7804}
23 = {UriComponentsBuilderMethodArgumentResolver@7805}
24 = {RequestParamMethodArgumentResolver@7806}
25 = {ServletModelAttributeMethodProcessor@7807} - 上述 resolver 的原理一般是根据参数前是否使用了 指定的注解 来判断是否该 resolver 是否支持解析该参数 或者 同时判断参数的类型是否满足要求
- 0 = {RequestParamMethodArgumentResolver@7782}
- 其次,找到合适的 resolver,即调用 resolveArgument 方法进行参数的解析,对于实体类型,这里对应处理的 resolver 为 ModelAttributeMethodProcessor,定位到具体的处理位置
核心处理
上述已经知道了,当传入类型为 Pojo 类时,确定的 resolver 为 ModelAttributeMethodProcessor,下面来看看其是怎么进行封装解析的
- org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 需要有 ModelAndViewContainer 与 WebDataBinderFactory
// 获取到参数的key,即参数名称,若标注了ModelAttribute,则获取其中的value值作为该key值
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// 1. 创建 attribute
attribute = createAttribute(name, parameter, binderFactory, webRequest);
// ...
}
if (bindingResult == null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
// 2. 发送请求的请求参数值进行封装
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
// 3. 获取绑定的结果,即绑定后的实体类
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
说明:
- createAttribute:创建出请求接口中的实体类参数的实体类,若实体类中有传参对应的构造函数,则直接赋予值
- bindRequestParameters:将发送请求的请求参数值进行解析,与 attribute 的其他属性进行绑定
- getBindingResult():获取绑定的结果,即上述两个方法完成后最终绑定的实体类
1、createAttribute
- org.springframework.web.method.annotation.ModelAttributeMethodProcessor#createAttribute
protected Object createAttribute(String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
// 获取到该参数的详细信息,包括 参数的位置 参数的类型 等相关信息
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 获取到该参数的具体类型
Class<?> clazz = nestedParameter.getNestedParameterType();
// 找到构造函数这个方法只对Kotlin有用,正常的情况直接返回null
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(clazz);
if (ctor == null) {
// 找到类中的构造函数,如果只有一个就获取一个,不然就获取所有
Constructor<?>[] ctors = clazz.getConstructors();
if (ctors.length == 1) {
ctor = ctors[0];
}else {
try {
ctor = clazz.getDeclaredConstructor();
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("No primary or default constructor found for " + clazz, ex);
}
}
}
// 根据构造方法实例化出一个实例
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
if (parameter != nestedParameter) {
attribute = Optional.of(attribute);
}
return attribute;
}
- org.springframework.web.method.annotation.ModelAttributeMethodProcessor#constructAttribute
protected Object constructAttribute(Constructor<?> ctor, String attributeName, MethodParameter parameter,
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
//...
// 如果没有参数,则通过clazz.newInstance方法实例出这个类
if (ctor.getParameterCount() == 0) {
// A single default constructor -> clearly a standard JavaBeans arrangement.
return BeanUtils.instantiateClass(ctor);
}
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
// 根据构造方法获取到该构造方法中的各个参数名称
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
// 根据构造方法获取到该构造方法中的各个参数的类型
Class<?>[] paramTypes = ctor.getParameterTypes();
Object[] args = new Object[paramTypes.length];
// 创建 WebDataBinder 数据绑定器,进行数据绑定
WebDataBinder binder = binderFactory.createBinder(webRequest, null, attributeName);
String fieldDefaultPrefix = binder.getFieldDefaultPrefix();
String fieldMarkerPrefix = binder.getFieldMarkerPrefix();
boolean bindingFailure = false;
Set<String> failedParams = new HashSet<>(4);
// 逐一处理构造方法获取到的参数名称,若传参有构造函数对应的key,则保存至args里
for (int i = 0; i < paramNames.length; i++) {
String paramName = paramNames[i];
Class<?> paramType = paramTypes[i];
// 通过 request.getParameterxxx来获取参数值
Object value = webRequest.getParameterValues(paramName);
// 如果没有获取到值(即没有传递该参数),尝试加上前缀 / 后缀来再次获取
if (value == null) {
if (fieldDefaultPrefix != null) {
value = webRequest.getParameter(fieldDefaultPrefix + paramName);
}
if (value == null && fieldMarkerPrefix != null) {
if (webRequest.getParameter(fieldMarkerPrefix + paramName) != null) {
value = binder.getEmptyValue(paramType);
}
}
}
try {
MethodParameter methodParam = new FieldAwareConstructorParameter(ctor, i, paramName);
// 如果 value 不能空也不是可选参数进行处理
if (value == null && methodParam.isOptional()) {
args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
}else {
// 通过 WebDataBinder数据绑定器 中的conversionService 中的 converters 进行数据类型转换
args[i] = binder.convertIfNecessary(value, paramType, methodParam);
}
} catch (TypeMismatchException ex) {
ex.initPropertyName(paramName);
args[i] = value;
failedParams.add(paramName);
binder.getBindingResult().recordFieldValue(paramName, paramType, value);
binder.getBindingErrorProcessor().processPropertyAccessException(ex, binder.getBindingResult());
bindingFailure = true;
}
}
// 数据绑定失败即抛出异常
if (bindingFailure) {
BindingResult result = binder.getBindingResult();
for (int i = 0; i < paramNames.length; i++) {
String paramName = paramNames[i];
if (!failedParams.contains(paramName)) {
Object value = args[i];
result.recordFieldValue(paramName, paramTypes[i], value);
validateValueIfApplicable(binder, parameter, ctor.getDeclaringClass(), paramName, value);
}
}
throw new BindException(result);
}
// 根据构造函数来实例出这个类,args里面的值作为构造函数的传入值,即实现了值得赋予
return BeanUtils.instantiateClass(ctor, args);
}
总结:上述方法的整个过程就实例化出一个 arttribute(实体类),若实体类中有传参对应的构造函数,则直接赋予值
// 例如有一个实体类User
public class User {
@NotNull(message = "id不能为空", groups = UpdateUser.class)
Integer userId;
@NotBlank(message = "姓名不能为空", groups = CreateUser.class)
String name;
String username;
@Email(message = "不满足邮箱格式")
private String email;
// 有一个 userId与email的构造函数
public User(@NotNull(message = "id不能为空", groups = UpdateUser.class) Integer userId, @Email(message = "不满足邮箱格式") String email) {
this.userId = userId;
this.email = email;
}
}
// 有一个接口
@GetMapping("/pojo_getValue")
public ResponseEntity<User> pojoGetValue(User user){
return new ResponseEntity<>(user , HttpStatus.OK);
}
// 请求为
http://localhost:8081/pojo_getValue?name=111&username=11122&userId=11
// 这里的Attribute为
User(userId=11, name=null, username=null, email=null)
// 请求为
http://localhost:8081/pojo_getValue?name=111&username=11122
// 这里的Attribute为
User(userId=null, name=null, username=null, email=null)
2、bindRequestParameters
- org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#bindRequestParameters
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
Assert.state(servletRequest != null, "No ServletRequest");
ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
// 核心方法,将参数值与实体类中的key绑定起来
servletBinder.bind(servletRequest);
}
- org.springframework.web.bind.ServletRequestDataBinder#bind
public void bind(ServletRequest request) {
// 获取到请求参数的key与value,保存至其 propertyValueList 中,每一个元素包含 参数的名称 参数的值
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
// 如果request.getAttribute有传递信息,也加入到 propertyValueList 数组中来
addBindValues(mpvs, request);
// 核心方法,进行绑定
doBind(mpvs);
}
- org.springframework.web.bind.WebDataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
// 检查是否有默认值,根据前缀检查
this.checkFieldDefaults(mpvs);
// 检查是否有默认值,根据后缀检查
this.checkFieldMarkers(mpvs);
super.doBind(mpvs);
}
- org.springframework.validation.DataBinder#doBind
protected void doBind(MutablePropertyValues mpvs) {
// 同理来检查
this.checkAllowedFields(mpvs);
this.checkRequiredFields(mpvs);
// apply
this.applyPropertyValues(mpvs);
}
- org.springframework.validation.DataBinder#applyPropertyValues
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields());
} catch (PropertyBatchUpdateException var7) {
//...
}
}
- org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
// 获取之前保存的请求参数数组 propertyValueList
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
// 逐一进行处理
for (PropertyValue pv : propertyValues) {
// 设置值
setPropertyValue(pv);
}
}
- org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)
public void setPropertyValue(PropertyValue pv) throws BeansException {
//...
nestedPa.setPropertyValue(tokens, pv);
//...
}
- org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.AbstractNestablePropertyAccessor.PropertyTokenHolder, org.springframework.beans.PropertyValue)
protected void setPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
//...
this.processLocalProperty(tokens, pv);
}
- org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
//...
Object oldValue = null;
// 请求参数值
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
if (isExtractOldValueForEditor() && ph.isReadable()) {
// 以前的值
oldValue = ph.getValue();
//...
}
valueToApply = convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
}
//...
}
// 真正的将转换后的值放入实体类对应的key中
ph.setValue(valueToApply);
}
- org.springframework.beans.AbstractNestablePropertyAccessor#convertForProperty
protected Object convertForProperty(String propertyName, @Nullable Object oldValue, @Nullable Object newValue, TypeDescriptor td) throws TypeMismatchException {
return this.convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
- org.springframework.beans.AbstractNestablePropertyAccessor#convertIfNecessary
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td) throws TypeMismatchException {
//...
try {
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
} catch (IllegalStateException | ConverterNotFoundException var8) {
//...
} catch (IllegalArgumentException | ConversionException var9) {
//...
}
}
- org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class, org.springframework.core.convert.TypeDescriptor)
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
//...
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
// 通过 conversionService 中的converters 进行转换
return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
} catch (ConversionFailedException var14) {
//...
}
//...
}
总结:上述这个方法,主要是处理接口的传参参数,逐一处理每个参数,通过与实体类中对应key的类型进行比较,通过converter进行值得转换,转换成功填充值Bean对应得key中,即完成实体类属性值的填充
传参为实体类的总结
接口传参为实体类时,例如:
@GetMapping("/pojo_getValue")
public ResponseEntity<User> pojoGetValue(User user){
return new ResponseEntity<>(user , HttpStatus.OK);
}
上述的整个核心处理的链路可以简单概括为以下几个部分:
- 首先确定了处理实体类类型传参的 resolver,为 ModelAttributeMethodProcessor,调用 resolveArgument 方法进行参数的解析
- 该方法首先,实例出一个实体类User,该实体类的属性初始值与其实现的构造器密切相关:
- 若只有无参构造器,就是一个值均为null的实体类
- 若有有参构造器,且接口调用传参有相应的key,则将值通过合适的 converter 进行类型转换后赋予值,若没有相应的key,则初始值为null
- 其次,该方法逐一处理接口调用的参数,与实体类的属性key对应,通过合适的converter 进行类型转换后赋予值
- 通过上述两步,实现最终的实体类,进行返回,从而完成封装
Resolver 介绍
根据上述的解析可以得知,参数的解析就是逐一判断参数的解析resolver,即 this.resolvers.supportsParameter(parameter) 是否为 true,默认的 this.resolvers 列表为下面的26个,分别对不同的参数类型进行处理
0 = {RequestParamMethodArgumentResolver@7782}
1 = {RequestParamMapMethodArgumentResolver@7783}
2 = {PathVariableMethodArgumentResolver@7784}
3 = {PathVariableMapMethodArgumentResolver@7785}
4 = {MatrixVariableMethodArgumentResolver@7786}
5 = {MatrixVariableMapMethodArgumentResolver@7787}
6 = {ServletModelAttributeMethodProcessor@7788}
7 = {RequestResponseBodyMethodProcessor@7789}
8 = {RequestPartMethodArgumentResolver@7790}
9 = {RequestHeaderMethodArgumentResolver@7791}
10 = {RequestHeaderMapMethodArgumentResolver@7792}
11 = {ServletCookieValueMethodArgumentResolver@7793}
12 = {ExpressionValueMethodArgumentResolver@7794}
13 = {SessionAttributeMethodArgumentResolver@7795}
14 = {RequestAttributeMethodArgumentResolver@7796}
15 = {ServletRequestMethodArgumentResolver@7797}
16 = {ServletResponseMethodArgumentResolver@7798}
17 = {HttpEntityMethodProcessor@7799}
18 = {RedirectAttributesMethodArgumentResolver@7800}
19 = {ModelMethodProcessor@7801}
20 = {MapMethodProcessor@7802}
21 = {ErrorsMethodArgumentResolver@7803}
22 = {SessionStatusMethodArgumentResolver@7804}
23 = {UriComponentsBuilderMethodArgumentResolver@7805}
24 = {RequestParamMethodArgumentResolver@7806}
25 = {ServletModelAttributeMethodProcessor@7807}
说明:
- 这26个解析参数的类可解析参数可以查看各个类中的 supportsParameter 方法,若返回值为true,即由该类进行解析(大概原理就是判断参数是否使用了 指定的注解 或者 同时判断参数的类型是否满足要求)
- 若由该类进行解析,则会调用各个类中的 resolveArgument 方法进行核心的参数解析
自定义 ArgumentResolver
如果我不想靠上述默认的一些规则来对传参进行解析,我们可以进行自定义ArgumentResolver
例如:对上述的实体类传参的resolver处理,我们不通过默认的进行处理,而是通过我们自定义的来处理,该如何解决呢?
1、编写 Resolver
上述默认的 26 个 resolver 都直接或者间接实现了 org.springframework.web.method.support.HandlerMethodArgumentResolver 接口,同理,如果我们想要自定义自己的resolver,可以编写 HandlerMethodArgumentResolver 的实现类,重写以下两个方法
- supportsParameter:是否用该 resolver 进行参数解析,这个方法很关键
- resolveArgument:参数的解析实现方法
package com.study.config;
import com.study.pojo.User;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
// 自定义 Resolver
public class MyResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 这里我是判断这个参数是否有我自定义的 MyAnnotation 注解
return parameter.hasParameterAnnotation(MyAnnotation.class);
// 也可以换成其他的,例如如果参数的类型是 Student 类的话,用这个resolver解析,怎么用就怎么写
// return methodParameter.getParameterType().equals(Student.class);
}
// 参数解析的具体实现,这里简单获取参数构造成一个实体类返回,可以根据需求写一些自定义的规则实现
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
Integer id = Integer.parseInt(nativeWebRequest.getParameter("id"));
String email = nativeWebRequest.getParameter("email");
String username = nativeWebRequest.getParameter("username");
String name = nativeWebRequest.getHeader("name");
User user = new User(id, email);
user.setUsername(username);
user.setName(name);
return user.toString();
}
}
package com.study.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "name";
boolean required() default false;
String defaultValue() default "default_name";
}
2、Resolver注入
这一步保证自定义resolver加入到后续的reslover列表中, 即上述讲解中的的 **this.resolvers.supportsParameter(parameter) ** ,this.resolvers 中
package com.study.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Configuration
public class MyWebMvcSupport extends WebMvcConfigurationSupport {
// 将自定义的resolver加入到处理resolver列表中
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyResolver());
}
}
3、使用
接口中直接使用
注意点:上述的 MyResolver#resolveArgument 返回的数据类型与这里的接口参数类型要保证一致,如果上述的 resolveArgument 返回的是 user 实体类,而不是user.toString(),这里以 username(字符串)接手就会报错。上述 返回的是 user.toString(),这里的 username 的接受值就为 user 对象的字符串
@GetMapping("/my_own_param_getValue")
public ResponseEntity<User> paramMyOwnValueGetValue(@MyAnnotation String username) {
User user = new User(1, "1");
user.setName(username);
user.setUsername(username);
return new ResponseEntity<>(user, HttpStatus.OK);
}
4、验证
调用该接口进行调试,可以看到:
- this.resolvers 列表的数据变为了27个,新增了 自定义 MyResolver
- 处理该接口参数的 resolver 为 自定义的 MyResolver
总结
看了看源码,其实接口传参默认支持的参数类型不多,例如泛型啥的好像都不支持(与泛型类型擦除有关),如果有一些复杂的接口传参类型,可以使用自定义的方法,通过实现 supportsParameter、resolveArgument 方法来完成我们的复杂参数解析需求