SpringMVC之自定义参数解析器(同时支持form提交和json提交)

场景:在重构公司的项目当中(将scalay语言转为java语言),由于历史遗留问题和语言,前端多个页面,对于同一接口有时使用json格式提交,有时使用表单形式提交,项目使用的是前后端分离模式,需要自定义参数解析器。

分析:SpringMVC中对json形式和表单形式的参数解析器都有实现,因而不需要我们自己去实现具体参数解析,而是在原有的功能方面进行增强,故此很容易想到可利用java中的装饰器模式去实现,具体实现如下。

 

首先:自定义注解,

import java.lang.annotation.*;

/**
 *
 * 标志者,我们将使用{@link com.lin.CustomMethodArgumentResolver}类去绑定参数
 * @author jianglinzou
 * @date 2019/4/13 下午3:20
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomParamBinding {



}

自定义的参数解析器如下:

 

import com.lin.CustomMethodArgumentResolverException;
import com.lin.annos.CustomParamBinding;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
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;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 一个HandlerMethodArgumentResolver的装饰器模式,能够支持json格式和表单形式的数据解析
 *
 * @author jianglinzou
 * @date 2019/4/13 上午1:17
 */
/**
 * 一个HandlerMethodArgumentResolver的装饰器模式,能够支持json格式和表单形式的数据解析
 *
 * @author jianglinzou
 * @date 2019/4/13 上午1:17
 */
@Component
public class CustomMethodArgumentResolver implements HandlerMethodArgumentResolver, Ordered, ApplicationListener {

    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    private List localResolver = new ArrayList<>();

    private boolean seal;

    private static Logger logger = LoggerFactory.getLogger(CustomMethodArgumentResolver.class);
//    @Autowired
//    RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    //对应json格式的数据解析,SpringMVC中已实现

    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;

    //对应表单格式的数据解析,SpringMVC中已实现
    private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {

//        !parameter.hasParameterAnnotation(PathVariable.class)
        if (parameter.hasParameterAnnotation(CustomParamBinding.class)) {
            logger.info("will user CustomMethodArgumentResolver to resolve parameter for:{}", parameter.getExecutable());
            return true;
        }

        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Class paramType = parameter.getParameterType();
        HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
        //根据请求头Content-Type,判断是什么形式提交
        String contentType = httpServletRequest.getHeader("Content-Type");
        if (Strings.isBlank(contentType)) { //没有,默认用表单
            return servletModelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
        //表单形式
        if (contentType.startsWith("application/x-www-form-urlencoded")) {
            logger.info("the contentType is application/x-www-form-urlencoded ");
            return servletModelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
        //json形式
        if (contentType.startsWith("application/json")) {
            logger.info("the contentType is application/json ");
            return requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
        return servletModelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        //
        //    return doContinueResolver(localResolver, parameter, mavContainer, webRequest, binderFactory);
    }


    private Object doContinueResolver(List localResolver, MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        //移除,自定义解析器,继续解析
        if (CollectionUtils.isEmpty(localResolver)) {
            logger.warn("localResolver is empty");
            return null;
        }
        localResolver.remove(this);
        for (HandlerMethodArgumentResolver resolver : localResolver) {
            if (resolver.supportsParameter(parameter)) {
                return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
            }
        }
        throw new CustomMethodArgumentResolverException("自定义解析参数出现异常,无法解析该参数:" + parameter.getParameter());

    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 1;
    }


    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (!seal) {
            //响应ApplicationReadyEvent事件,表明tomcat(jetty)容器已将上下文填充完毕,从而从容器中获取json和表单的参数解析器
            if (event instanceof ApplicationReadyEvent) {
                System.out.println("--------");
                RequestMappingHandlerAdapter requestMappingHandlerAdapter = (RequestMappingHandlerAdapter) ((ApplicationReadyEvent) event).getApplicationContext().getBean("requestMappingHandlerAdapter");
                this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
                if (Objects.isNull(requestMappingHandlerAdapter)) {
                    throw new RuntimeException("自定义参数解析器加载失败");
                }
                for (HandlerMethodArgumentResolver resolver : requestMappingHandlerAdapter.getArgumentResolvers()) {
                    if (resolver instanceof RequestResponseBodyMethodProcessor) { //获取json形式的解析器
                        this.requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) resolver;
                        continue;
                    }
                    if (resolver instanceof ServletModelAttributeMethodProcessor) { //获取表单形式的解析器
                        this.servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor) resolver;
                    }

                }
                localResolver.addAll(requestMappingHandlerAdapter.getArgumentResolvers());

                this.seal = true;
            }
        }
    }
}

其中CustomMethodArgumentResolverException如下:

/**
 * @author jianglinzou
 * @date 2019/4/13 下午10:49
 */
public class CustomMethodArgumentResolverException extends Exception {

    public CustomMethodArgumentResolverException(String msg) {
        super(msg);
    }
}

在最后在配置文件中添加我们的自定义解析器:

import com.lin.CustomMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.OrderComparator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author jianglinzou
 * @date 2019/4/13 上午2:41
 */
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
    @Resource
    private CustomMethodArgumentResolver customMethodArgumentResolver;

    @Override
    protected void addArgumentResolvers(List argumentResolvers) {
        argumentResolvers.add(customMethodArgumentResolver);
        OrderComparator.sort(argumentResolvers);
//        Collections.sort(argumentResolvers,OrderComparator);
        // 注册Spring data jpa pageable的参数分解器
//        argumentResolvers.add(new PageableHandlerMethodArgumentResolver());
    }


}

 

配置完成后,写一个简单的controller如下

@Controller
@RequestMapping("/")
public class TestController {

    private static final Logger logger = LoggerFactory.getLogger(TestController.class);

//    @Autowired
//    AmazonSummaryMapper amazonSummaryMapper;

    @PostMapping("/test/helloWorld")
    @ResponseBody
    public String returnHelloWorld(@CustomParamBinding Account account) {
        List sellerId = new ArrayList();
        sellerId.add("A7DCHBVITGESC");
//        sellerId.add("A16W335XFOTZRV");
        return account.getAccountType();
    }

}

验证过后,我们发现,该接口即支持表单形式提交,又支持json格式提交

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(SpringMVC)