前言

之前咱们的文章记一次springboot我的项目自定义HandlerMethodArgumentResolver不失效起因与解法开端留了一个思考题:在咱们我的项目中如何优雅批改或者填充申请参数,本期就来揭晓这个谜底

办法一:自定义HandlerMethodArgumentResolver

执行步骤:

1、自定义HandlerMethodArgumentResolver类
public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {    private HandlerMethodArgumentResolver handlerMethodArgumentResolver;    public UserHandlerMethodArgumentResolver(HandlerMethodArgumentResolver handlerMethodArgumentResolver) {        this.handlerMethodArgumentResolver = handlerMethodArgumentResolver;    }    @Override    public boolean supportsParameter(MethodParameter parameter) {        return parameter.hasParameterAnnotation(RequestBody.class) &&               User.class.isAssignableFrom(parameter.getParameterType());    }    @Override    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {        User user = (User) handlerMethodArgumentResolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);        if(StringUtils.isBlank(user.getId())){            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);            String id = request.getHeader("id");            user.setId(id);        }        System.out.println(user);        return user;    }}
2、将自定义的HandlerMethodArgumentResolver增加进行argumentResolvers
@Configurationpublic class HandlerMethodArgumentResolverAutoConfiguration implements InitializingBean {    @Autowired    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;    @Override    public void afterPropertiesSet() throws Exception {        List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();        List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<>();        for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {            if(argumentResolver instanceof RequestResponseBodyMethodProcessor){                 customArgumentResolvers.add(new UserHandlerMethodArgumentResolver(argumentResolver));            }            customArgumentResolvers.add(argumentResolver);        }        requestMappingHandlerAdapter.setArgumentResolvers(customArgumentResolvers);    }}

至于为啥这么搞,而不是通过

@Configurationpublic class WebConfig implements WebMvcConfigurer {        @Override    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {        resolvers.add();    }}

答案就在记一次springboot我的项目自定义HandlerMethodArgumentResolver不失效起因与解法这篇文章中

3、测试
public class MetaInfo {    private String id;    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }}
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class User extends MetaInfo{    private String username;}
@RestController@RequestMapping("user")public class UserController {    @PostMapping("add")    public User add(@RequestBody User user){        return user;    }}

办法二:自定义RequestBodyAdvice

1、自定义RequestBodyAdvice
@RestControllerAdvicepublic class ProductRequestBodyAdvice implements RequestBodyAdvice {    @Override    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {        return methodParameter.hasParameterAnnotation(RequestBody.class) &&               Product.class.isAssignableFrom(methodParameter.getParameterType());    }    @Override    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {        return inputMessage;    }    @Override    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {        Product product = (Product) body;        if(StringUtils.isBlank(product.getId())){            String id = inputMessage.getHeaders().getFirst("id");            product.setId(id);        }        System.out.println(product);        return product;    }    @Override    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {        return body;    }}
2、测试
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class Product extends MetaInfo{    private String productName;}
@RestController@RequestMapping("product")public class ProductController {    @PostMapping("add")    public Product add(@RequestBody Product product){        return product;    }}

办法三:自定义过滤器 + 自定义HttpServletRequestWrapper

1、自定义HttpServletRequestWrapper
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {    private String body;    @SneakyThrows    public CustomHttpServletRequestWrapper(HttpServletRequest request) {        super(request);        //获取申请body        byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());        body = new String(bodyBytes, request.getCharacterEncoding());    }    @Override    public ServletInputStream getInputStream() throws IOException {        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());        ServletInputStream servletInputStream = new ServletInputStream() {            @Override            public boolean isFinished() {                return false;            }            @Override            public boolean isReady() {                return false;            }            @Override            public void setReadListener(ReadListener readListener) {            }            @Override            public int read() throws IOException {                return byteArrayInputStream.read();            }        };        return servletInputStream;    }    @Override    public BufferedReader getReader() throws IOException {        return new BufferedReader(new InputStreamReader(this.getInputStream()));    }    public String getBody() {        return this.body;    }    public void setBody(String body) {        this.body = body;    }}
2、自定义过滤器
public class OrderFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        ServletRequest requestWrapper = null;        if(servletRequest instanceof HttpServletRequest) {            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;            requestWrapper = new CustomHttpServletRequestWrapper(httpServletRequest);            //当header的type为filter,由filter负责填充,否则由拦截器负责            if(Constant.HEADER_VALUE_TYPE_FILTER.equalsIgnoreCase(httpServletRequest.getHeader(Constant.HEADER_KEY_TYPE))){                System.out.println(">>>>>>>>>>> fillBodyWithId by OrderFilter");                RequestBodyUtil.fillBodyWithId((CustomHttpServletRequestWrapper) requestWrapper);            }        }        if(requestWrapper == null) {            //避免流读取一次就没有了,将流传递上来            filterChain.doFilter(servletRequest, servletResponse);        } else {            filterChain.doFilter(requestWrapper, servletResponse);        }    }    @Override    public void destroy() {    }}

批改申请体外围代码

public final class RequestBodyUtil {    private RequestBodyUtil(){}    public static void fillBodyWithId(CustomHttpServletRequestWrapper customHttpServletRequestWrapper){        String body = customHttpServletRequestWrapper.getBody();        if(JSONUtil.isJson(body)){            Order order = JSON.parseObject(body, Order.class);            if(ObjectUtil.isNotEmpty(order) && StringUtils.isBlank(order.getId())){                String id = ((HttpServletRequest)customHttpServletRequestWrapper.getRequest()).getHeader(Constant.HEADER_KEY_ID);                order.setId(id);                String newBody = JSON.toJSONString(order);                customHttpServletRequestWrapper.setBody(newBody);                System.out.println(">>>>>>>>>>>>> newBody----> " + newBody);            }        }    }}
3、注册filter
  @Bean    public FilterRegistrationBean servletRegistrationBean() {        OrderFilter orderFilter = new OrderFilter();        FilterRegistrationBean bean = new FilterRegistrationBean<>();        bean.setFilter(orderFilter);        bean.setName("orderFilter");        bean.addUrlPatterns("/order/*");        bean.setOrder(Ordered.LOWEST_PRECEDENCE);        return bean;    }
4、测试
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class Order extends MetaInfo{    private String orderName;}
@RestController@RequestMapping("order")public class OrderController {    @PostMapping("add")    public Order add(@RequestBody Order order){        return order;    }}

办法四:自定义拦截器 + 自定义过滤器 + 自定义HttpServletRequestWrapper

1、自定义HttpServletRequestWrapper

代码同办法三,他的作用在办法四次要起到批改body参数的作用

2、自定义过滤器

代码同办法三,他的作用次要解决Required request body is missing:问题

3、自定义拦截器
public class OrderHandlerInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if(handler instanceof HandlerMethod){            HandlerMethod handlerMethod = (HandlerMethod) handler;            for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {                if(Order.class.isAssignableFrom(methodParameter.getParameterType())){                    if(request instanceof CustomHttpServletRequestWrapper){                        CustomHttpServletRequestWrapper customHttpServletRequestWrapper = (CustomHttpServletRequestWrapper) request;                        RequestBodyUtil.fillBodyWithId(customHttpServletRequestWrapper);                    }                }            }        }        return true;    }}
4、配置拦截器
public class OrderHandlerInterceptorAutoConfiguration implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(orderHandlerInterceptor()).addPathPatterns("/order/**");    }    @Bean    @ConditionalOnMissingBean    public OrderHandlerInterceptor orderHandlerInterceptor(){        return new OrderHandlerInterceptor();    }    }
5、测试

测试示例同办法三

办法五 通过AOP实现

1、编写AOP切面
@Aspect@Componentpublic class MemberAspect {    /**     *     * @param pjp     * @return     *     * @within 和 @target:带有相应标注的所有类的任意办法,比方@Transactional     * @annotation:带有相应标注的任意办法,比方@Transactional     * @within和@target针对类的注解,@annotation针对办法的注解     *     * @args:参数带有相应标注的任意办法,比方@Transactiona     */    @SneakyThrows    @Around(value = "@within(org.springframework.web.bind.annotation.RestController)")    public Object around(ProceedingJoinPoint pjp){        MethodSignature methodSignature =  (MethodSignature)pjp.getSignature();        HandlerMethod handlerMethod = new HandlerMethod(pjp.getTarget(),methodSignature.getMethod());        MethodParameter[] methodParameters = handlerMethod.getMethodParameters();        MethodParameterUtil.fillParamValueWithId(methodParameters,pjp.getArgs(), Member.class);        Object result = pjp.proceed();        return result;    }}

批改参数的外围代码

public final class MethodParameterUtil {    private MethodParameterUtil(){}    public static void fillParamValueWithId(MethodParameter[] methodParameters,Object[] args,Class<? extends MetaInfo> clz){        if(ArrayUtil.isNotEmpty(methodParameters)){            for (MethodParameter methodParameter : methodParameters) {                if (methodParameter.getParameterType().isAssignableFrom(clz)                        && methodParameter.hasParameterAnnotation(InjectId.class)) {                    Object obj = args[methodParameter.getParameterIndex()];                    if(obj instanceof MetaInfo){                        MetaInfo metaInfo = (MetaInfo) obj;                        if(StringUtils.isBlank(metaInfo.getId())){                            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();                            String id = servletRequestAttributes.getRequest().getHeader(Constant.HEADER_KEY_ID);                            metaInfo.setId(id);                            System.out.println(">>>>>>>>>>>>> newObj----> " + JSON.toJSONString(obj));                        }                    }                }            }        }    }}
2、测试
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class Member extends MetaInfo{    private String memberName;}
@RestController@RequestMapping("member")public class MemberController {    @PostMapping("add")    public Member add(@RequestBody @InjectId Member member){        return member;    }}

总结

本文介绍了5种批改或者填充申请参数的办法,这边有几个小细节点需注意一下,通过自定义HandlerMethodArgumentResolver这种形式,如果办法同时存在spring默认自带的HandlerMethodArgumentResolver和自定义HandlerMethodArgumentResolver,如果间接通过重写WebMvcConfigurer增加argumentResolver这种形式,则自定义HandlerMethodArgumentResolver会生效。其次通过RequestBodyAdvice这种形式只实用于办法参数加了@RequestBody 或 HttpEntity 办法参数。最初下面这几种形式,除了用来批改或者填充参数,他还能够用来做申请参数的校验,感兴趣的敌人能够本人扩大一下

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-argument-resolver