场景:在重构公司的项目当中(将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<HandlerMethodArgumentResolver> 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<HandlerMethodArgumentResolver> 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<HandlerMethodArgumentResolver> 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格式提交
版权声明:本文为qq_32459653原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。