在Springboot使用FastJson解析Controller傳入參數

實現目的

使用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解析過程分析

  1. 配置類實現WebMvcConfigurer接口的下述方法
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
    
    來進行配置
  2. 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;
    }
    
  3. 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;
    	}
    
  4. 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);
    	}
    
  5. 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;
    	}
    

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章