使用FastJson實現Springboot中的HandlerMethodArgumentResolver
實現目的
使用Fastjson來解析Springboot中Controller
的參數
實現代碼
FastJsonParse
package com.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* description: FastJsonParse 註解標記的參數將使用FastJson解析
*
* @date: 2020-03-31 10:04
* @author: shangjie
* @version: 1.0
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface FastJsonParse {
/**
* 解析帶泛型的類
*
*/
boolean useGenericType() default false;
}
FastJsonArgumentResolver
package com.common;
import com.alibaba.fastjson.JSON;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.MapMethodProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
/**
* description: FastJsonArgumentResolver
*
* @date: 2020-03-31 10:03
* @author: shangjie
* @version: 1.0
*/
public class FastJsonArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(FastJsonParse.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// content-type不是json的不處理
if (!request.getContentType().contains("application/json")) {
return null;
}
// 把request的body讀取到StringBuilder
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int rd;
while((rd = reader.read(buf)) != -1){
sb.append(buf, 0, rd);
}
// 利用fastjson轉換爲對應的類型
if(JSONObjectWrapper.class.isAssignableFrom(parameter.getParameterType())){
return new JSONObjectWrapper(JSON.parseObject(sb.toString()));
} else {
// 判斷是否解析的是帶泛型的類
FastJsonParse fastJsonParse = parameter.getParameterAnnotation(FastJsonParse.class);
if (fastJsonParse.useGenericType()) {
return JSON.parseObject(sb.toString(), parameter.getGenericParameterType());
} else {
return JSON.parseObject(sb.toString(), parameter.getParameterType());
}
}
}
}
JSONObjectWrapper
package com.common;
import com.alibaba.fastjson.JSONObject;
/**
* description: JSONObjectWrapper
*
* JSONObject實現了Map接口,所以Spring MVC的默認處理器MapMethodProcessor會先處理,
* 這裏封裝起來避免原始類型是JSONObject的參數被MapMethodProcessor處理
*
* @date: 2020-03-31 10:08
* @author: shangjie
* @version: 1.0
*/
public class JSONObjectWrapper {
private JSONObject jsonObject;
public JSONObjectWrapper(JSONObject jsonObject) {
this.jsonObject = jsonObject;
}
public JSONObject getJSONObject() {
return jsonObject;
}
}
Springboot配置
package com;
import com.common.FastJsonArgumentResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ApplicationConfiguration implements WebMvcConfigurer {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfiguration.class);
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new FastJsonArgumentResolver());
}
}
使用HandlerMethodArgumentResolver解析過程分析
- 配置類實現
WebMvcConfigurer
接口的下述方法
來進行配置public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
- spring使配置
ArgumentResolvers
使用的是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
類中的getDefaultArgumentResolvers
方法private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
- spring處理http請求過程中在
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
方法裏解析request中傳入的參數private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); // 這裏解析參數 args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; }
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
方法裏選定了resolver,並進行解析public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 這裏選擇resolver HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } // 這裏解析 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver
方法選擇resolver。這裏也解釋了定義JSONObjectWrapper
類的意義,由於JSONObject
實現了Map接口,而在getDefaultArgumentResolvers
方法中註冊的解析Map的resolverMapMethodProcessor
在自定義resolver之前。所以將JSONObject
封裝,避免原始類型是JSONObject
的參數被MapMethodProcessor處理被MapMethodProcessor
解析private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } // 這裏從註冊的resolver中順序選擇支持解析對應parameter的resolver if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }