Spring MVC 自定义参数解析器

本文介绍了如何在Spring MVC中自定义参数解析器,以处理特定格式如Properties的数据。通过分析DispatcherServlet、RequestMappingHandlerAdapter和RequestResponseBodyMethodProcessor的工作原理,作者展示了如何创建一个名为RequestParamMethodPropertiesBodyArgumentConvertObjectResolver的自定义解析器,并将其配置到Spring MVC中。文章还提供了测试示例和Maven配置信息。
摘要由CSDN通过智能技术生成

SpringMVC

spring mvc 是一个非常好用的框架,傻瓜式的操作,深受人们的喜爱。
框架自带的功能基本能够满足市面上百分之八九十的需求,但总有一些奇葩的需求,这时候我们就要自定义一些功能(搞事情)

DispatcherServlet

DispatcherServlet这个类在写web.xml的时代使用框架时是要注册到servlet当中的,当servlet3.0出来之后javaEE支持动态增加servlet,因此springBoot里直接加个@Controller注解加个@XXXMapping注解然后main里面SpringApplication.run(XXX.class,args)无脑启动就能直接开启一个服务。如果要自定义一个参数处理器就要看下DispatcherServlet这个类弄了什么幺蛾子。

打开源码,他这里意思是说 用Servlet 3.0+,能支持程序里注册Servlet实例。大概是这个样子,英语渣见谅
DispatcherServlet说明

然后看到类里面有个 doService方法,肯定是继承上一家的,不用管那么多,反正请求来了他会调这个方法。
doService方法里面调用了一个doDispatch方法
DispatcherServlet的doService方法

传送到doDispatch方法定义,里面调用了getHandler方法,这里面取出一个handler,这里的handler指的就是我们写了 @RequestMapping("/helloWorld")这种方法,可能也有一些别的handler,比如它里面预定义的"/error",他这里应该是解析地址取出跟请求相对应的handler,这里handlers应该是在启动的时候就准备好了的(也许是在别的时候),存在一个Map容器里。后面调用了getHandlerAdapter方法,取出一个Adapter(适配器)
doDispatch方法定义

跳到getHandlerAdapter方法定义for循环,看哪个adapter.supports(handler)返回的是true就选用哪个adapter,这里一定要选中一个adapter,不然程序报错。为了看到取出的是什么Adapter,我在这里打了一个断点,用Postman请求一下看一下选中的适配器,可以看到选中的是RequestMappingHandlerAdapter类
在这里插入图片描述

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter类里面定义了参数解析器集合,还有返回值处理,我们这里关注解析器。看HandlerMethodArgumentResolverComposite这个类,这里用了组合模式,具体实现委托这个类去做。
在这里插入图片描述

这个类也是有相应的getArgumentResolver(取出解析器)方法,跟前面的getHandler和getHandlerAdapter是一个尿性,这里不再提。可以看到解析器的类是用 HandlerMethodArgumentResolver 表示的。那么自定义请求参数解析器就好办了,写一个符合这样的类在这里插入图片描述

一般我们写一个请求:

/**
 * User {@link RestController}
 *
 * @author : guanzheng
 * @since : 2018-12-17 16:35
 **/
@RestController
public class UserRestController {
   

    @RequestMapping(value = "/getUser", method = {
   RequestMethod.GET, RequestMethod.POST})
    public String getUserInfo(@RequestBody Person person) {
   
        return "name is: " + person.getName() + ",age is " + person.getAge();
    }

}

然后 Person定义

/**
 * @author : guanzheng
 * @since : 2018-12-21 16:07
 **/
public class Person {
   

    private String name;
    private int age;

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public int getAge() {
   
        return age;
    }

    public void setAge(int age) {
   
        this.age = age;
    }
}

用Postman请求,在Body里raw里面填写json数据,返回如下图
在这里插入图片描述

回到HandlerMethodArgumentResolver
Ctrl/Command + H查看继承链,打了@RequestBody的注解的方法参数是RequestResponseBodyMethodProcessor类支持的处理。
在这里插入图片描述

RequestResponseBodyMethodProcessor

打开RequestResponseBodyMethodProcessor
这个@Override
supportsParameter(MethodParameter parameter)
方法体写了只要参数标注了@RequestBody注解就能处理,说明请求参数前标注@RequestBody是这个类来处理的
在这里插入图片描述

RequestResponseBodyMethodProcessor这个类应该能处理大部分Body,看下他的支持
在这里插入图片描述

有熟悉的json,xml,还有ByteArray,Buffer等不知道干嘛用的233
在这里插入图片描述

自定义解析器

接下来可以模仿他写一个自定义的解析器,比如解析Properties格式,可以继续使用RequestResponseBodyMethodProcessor类实现HttpMessageConverter接口,也可以重新实现HandlerMethodArgumentResolver接口。我选择重新实现HandlerMethodArgumentResolver接口。
模仿@RequestBody这种写法,新建一个bind.annotation的包,在bind.annotation包下新建一个RequestPropertiesBody 注解类

package com.my.web.bind.annotation;

import java.lang.annotation.*;

/**
 * @author : guanzheng
 * @since : 2018-12-24 11:58
 **/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPropertiesBody {
   

    boolean required() default true;

}

不要问我为什么这样分,我是抄的spring的。。。
在这里插入图片描述

再建一个method.annotation包,包下面新建一个RequestParamMethodPropertiesBodyArgumentConvertObjectResolver类,并继承HandlerMethodArgumentResolver接口,当然,包名和类名都是借鉴(抄)的spring的写法。
supportsParameter方法的实现决定了spring是否选用这个类作为参数解析器,因此这个方法比较重要。配合前面写的RequestPropertiesBody 注解,这里只要方法参数前面加了RequestPropertiesBody 注解就支持解析,跟RequestResponseBodyMethodProcessor类的supportsParameter的方法基本一样的实现。resolveArgument方法是返回添加了RequestPropertiesBody参数的实体

/**
 * @author : guanzheng
 * @since : 2018-12-24 11:56
 **/
public class RequestParamMethodPropertiesBodyArgumentConvertObjectResolver 
implements HandlerMethodArgumentResolver {
   

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
   
        return parameter.hasParameterAnnotation(RequestPropertiesBody.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
     ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
      WebDataBinderFactory binderFactory) throws Exception {
   
        
        return null;
    }

先获取类的Class,他提供了MethodParameter 参数,可以用parameter.getParameterType();获取

Class<?> parameterType = parameter.getParameterType();

再装载Properties类,NativeWebRequest 参数可以获取request,写一个getProperties方法

private Properties getProperties(NativeWebRequest webRequest) throws IOException {
   
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);

        Properties properties = new Properties();


        if (request != null) {
   


            // Servlet Request API

            String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
            // 获取 Content-Type 请求头
            MediaType mediaType = MediaType.parseMediaType(contentType);

            Charset charset = mediaType.getCharset();

            // 当 charset 为空时,就使用UTF-8
            charset = charset == null ? StandardCharsets.UTF_8 : charset;

            // 字节流
            InputStream inputStream = request.getInputStream();


            InputStreamReader reader = new InputStreamReader(inputStream, charset);

            // 加载字符流成为 Properties 对象
            properties.load(reader);

            inputStream.close();


        } else {
   
            throw new IllegalArgumentException();
        }

        return properties;
    }

有了Properties,就可以取出相应的属性。把Properties的属性值写到对象相应的字段里,为了看对象有那些字段,用反射获取

//FieldUtil所在包是  sun.reflect.misc.FieldUtil
Field[] fields = FieldUtil.getDeclaredFields(parameterType);

把要返回的对象准备好

//ClassUtils 所在的类是 org.springframework.objenesis.instantiator.util.ClassUtils
Object result = ClassUtils.newInstance(parameterType);

获取对象的setter方法,没有写getter和setter的类我是不管的,谁让不按照代码规范来写的 (=゚ω゚)傲娇脸

    private Method getSetterMethod(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值