springmvc自定义参数解析器/类型转换器

Posted 好大的月亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springmvc自定义参数解析器/类型转换器相关的知识,希望对你有一定的参考价值。

概述

有些时候我们需要对请求的入参做自定义的处理,比较常见的就是字符串反序列化时间类型了,常用的像@DateTimeFormat注解,但是这需要在每个入参的属性上都加上这个注解,比较费手,那么我们就可以注册一个通用的自定义类型转换器来做这个时间的转换。

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;

自定义类型转换器demo入门认识

使用类型转换器org.springframework.core.convert.converter.Converter做统一时间处理

一个普通的get请求

package com.fchan.controller;

import com.fchan.params.TestDateParams;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("selfSpringMVC")
public class SelfSpringMVCController 


    @GetMapping("testDate")
    public Object testDate(TestDateParams params)
        System.out.println(params);
        return null;
    


一个普通的入参

package com.fchan.params;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

@Data
public class TestDateParams implements Serializable 

    private static final long serialVersionUID = -6513336502447867393L;

    private Date date;

    private LocalDateTime localDateTime;

    private LocalDate localDate;


自定义类型转换

package com.fchan.convert;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class SelfDateConvert implements Converter<String, Date> 


    @Override
    public Date convert(String source) 
        Assert.notNull(source, "时间不能为空");

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        try 
            return df.parse(source);
         catch (ParseException e) 
            e.printStackTrace();
            return null;
        
    



package com.fchan.convert;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;

@Component
public class SelfLocalDateConvert implements Converter<String, LocalDate> 


    @Override
    public LocalDate convert(String source) 
        Assert.notNull(source, "时间不能为空");

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return LocalDate.parse(source, df);
    


package com.fchan.convert;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class SelfLocalDateTimeConvert implements Converter<String, LocalDateTime> 


    @Override
    public LocalDateTime convert(String source) 
        Assert.notNull(source, "时间不能为空");

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return LocalDateTime.parse(source, df);
    

将我们的自定义参数转换添加到spring的参数解析链中

package com.fchan.config;


import com.fchan.convert.SelfDateConvert;
import com.fchan.convert.SelfLocalDateConvert;
import com.fchan.convert.SelfLocalDateTimeConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Configuration
public class WebMvcConfigurerAdapter implements WebMvcConfigurer 

    @Autowired
    private SelfDateConvert selfDateConvert;
    @Autowired
    private SelfLocalDateConvert selfLocalDateConvert;
    @Autowired
    private SelfLocalDateTimeConvert selfLocalDateTimeConvert;


    @Override
    public void addFormatters(FormatterRegistry registry) 
        registry.addConverter(selfDateConvert);
        registry.addConverter(selfLocalDateConvert);
        registry.addConverter(selfLocalDateTimeConvert);
    



SpringMVC 执行流程

套用一张网图

参数解析器

所有的参数解析器都实现了HandlerMethodArgumentResolver这个接口

这是一个典型的策略模式接口,判断当前的方法形参类型是否支持,如果支持就使用该实现类的参数解析器对其解析.

org.springframework.web.servlet.DispatcherServlet调用方法前肯定需要解析一下请求中的参数,转成实际controller中的类型

自定义参数解析器demo

平时接收post请求我们都是用的@requestBody接收的请求体,但有些时候想对请求体中的参数在进入controller之前做一些自定义的转换,这个时候我们就可以自己实现一个注解来解析请求体中的参数。

还是以上面类型转换器中的入参为例,只是这回把入参放入http body中去.

自定义注解

package com.fchan.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentTime 

自定义解析器解析http body

package com.fchan.methodArgumentResolver;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fchan.annotation.CurrentTime;
import com.fchan.params.TestDateParams;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
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 javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
public class CurrentTestTimeHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver 

    @Autowired
    private ObjectMapper myObjectMapper;


    /**
     * 用于判定是否需要处理该参数分解,返回 true 为需要,并会去调用下面的方法resolveArgument。
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) 
        return parameter.hasParameterAnnotation(CurrentTime.class)
                &&
               parameter.getParameterType().isAssignableFrom(TestDateParams.class);
    

    /**
     * 真正用于处理参数分解的方法,返回的 Object 就是 controller 方法上的形参对象。
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception 

        //webRequest.getHeader("xxx")
        //Map<String, String[]> parameterMap = webRequest.getParameterMap();

        String requestBody = getRequestBody(webRequest);

        return myObjectMapper.readValue(requestBody, TestDateParams.class);
    

    private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";

    private String getRequestBody(NativeWebRequest webRequest)
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        String jsonBody = (String) webRequest.getAttribute(JSONBODYATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
        if (jsonBody == null) 
            try 
                jsonBody = IOUtils.toString(servletRequest.getInputStream());
                webRequest.setAttribute(JSONBODYATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
             catch (IOException e) 
                throw new RuntimeException(e);
            
        
        return jsonBody;
    


注册解析器

成功解析

以上是关于springmvc自定义参数解析器/类型转换器的主要内容,如果未能解决你的问题,请参考以下文章

Spring自定义argumentResolver参数解析器

SpringMVC—— 视图视图解析器自定义转换器格式化转换

springboot自定义参数解析器

SpringMVC ArgumentREsoler(方法参数解析器)

springMVC自定义方法属性解析器

springmvc 参数解析绑定原理