Controller 全类型参数解析器 - 枚举、日期、支持自定义类型(需要在笔者自己写的 ConvertUtil 中实现转换), 可反序列化前端传入 Enum 值,超低耦合度!非侵入式代码!

Spring Boot Controller 全类型参数解析器 - 枚举、日期、支持自定义类型(需要在笔者自己写的 ConvertUtil 中实现转换)

前言

Controller 处理 Get / Post 等请求时,将前端传递的参数反序列化为 Enum 类型。

本文将使用笔者自己写的 ConvertUtil 工具类进行基本的 ObejctEnum<E> 类型转换。

传送门 : 自定义 ConvertUtil 值转换工具类

一共分三步:

  1. 创建自定义参数解析器
  2. 配置参数解析器
  3. 声明并管理自动配置类

创建自定义参数解析器

package com.demo.common.security.resolver;

import com.demo.common.core.utils.converter.ConvertUtil;
import org.springframework.core.MethodParameter;
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 java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 自定义参数解析器
 *
 * @author HeHTao
 * @date 2024/05/20
 */
public class CustomArgumentResovler implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 最终都走该参数解析器
        return true;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter
            , ModelAndViewContainer mavContainer
            , NativeWebRequest webRequest
            , WebDataBinderFactory binderFactory) {

        Class<?> paramType = parameter.getParameterType();
        Map<String, String[]> paramsMap = webRequest.getParameterMap();

        // 已在工具中实现的转换类型,直接转换
        if (ConvertUtil.isConvertedClass(paramType)) {
            Object value = getValue(paramType, paramsMap, parameter.getParameterName());
            return ConvertUtil.convert(paramType, value);
        }
        // 不在工具中实现的转换类型,需要反序列化后转换

        // 获取属性列表
        List<Field> fieldList = ConvertUtil.getAllFields(paramType);
        // 构建实例
        Object result = ConvertUtil.getDefaultValue(paramType);
        // 获取所有属性,并反序列化后赋值
        for (Field field : fieldList) {
            Object value = getValue(field.getType(), paramsMap, field.getName());
            ConvertUtil.setFieldValue(result, field, value);
        }

        return result;
    }

    /**
     * 获取参数值
     *
     * @param clazz     参数类型
     * @param paramsMap 参数 Map
     * @param paramName 参数名
     * @return
     */
    private Object getValue(Class<?> clazz, Map<String, String[]> paramsMap, String paramName) {
        if (Map.class.isAssignableFrom(clazz)) {
            // 处理 Map 类型的字段
            return convertToMap(paramsMap, paramName);
        } else if (clazz.isArray()) {
            // 处理数组类型的字段
            String[] array = paramsMap.get(paramName);
            return null == array ? convertToArray(paramsMap, paramName, clazz.getComponentType()) : array;
        } else {
            // 处理普通字段
            String[] value = paramsMap.get(paramName);
            return null == value ? paramsMap.get("params[" + paramName + "]") : value;
        }
    }

    /**
     * 将匹配指定前缀的参数转换为 Map
     *
     * @param paramsMap 所有请求参数的 Map
     * @param prefix    前缀
     * @return 转换后的 Map
     */
    private Map<String, Object> convertToMap(Map<String, String[]> paramsMap, String prefix) {
        Map<String, Object> resultMap = new HashMap<>();
        String prefixWithBracket = prefix + "[";
        for (Map.Entry<String, String[]> entry : paramsMap.entrySet()) {
            String key = entry.getKey();
            if (key.startsWith(prefixWithBracket)) {
                String mapKey = key.substring(prefixWithBracket.length(), key.length() - 1);
                resultMap.put(mapKey, entry.getValue().length > 1 ? entry.getValue() : entry.getValue()[0]);
            }
        }
        return resultMap;
    }

    /**
     * 将匹配指定前缀的参数转换为数组
     *
     * @param paramsMap     所有请求参数的 Map
     * @param prefix        前缀
     * @param componentType 数组的组件类型
     * @return 转换后的数组
     */
    public static Object convertToArray(Map<String, String[]> paramsMap, String prefix, Class<?> componentType) {
        String prefixWithBracket = prefix + "[";
        int count = 0;

        // 计算匹配前缀的参数个数
        for (String key : paramsMap.keySet()) {
            if (key.startsWith(prefixWithBracket)) {
                count++;
            }
        }

        // 创建数组
        Object array = Array.newInstance(componentType, count);
        int index = 0;

        // 填充数组
        for (Map.Entry<String, String[]> entry : paramsMap.entrySet()) {
            String key = entry.getKey();
            if (key.startsWith(prefixWithBracket)) {
                String[] values = entry.getValue();
                Object value = ConvertUtil.convert(componentType, values.length > 1 ? values : values[0]);
                Array.set(array, index++, value);
            }
        }

        return array;
    }

}

配置参数解析器

使用 WebMvcConfigurer 需要在 pom 引入依赖 (如有需要)。(实测在 common-core 中引用会导致 auth 报错,所以在 security 模块中的 WebMvcConfig 中直接添加了该方法,不会报错)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
</dependency>

然后可以配置 Spring Boot 的解析器:

package com.demo.common.security.config;

import com.demo.common.security.resolver.CustomArgumentResovler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * @author HeHTao
 * @description 拦截器配置
 * @date 2024/05/17
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // 添加自定义参数解析器
        resolvers.add(new EnumArgumentResolver());
    }
}

声明并管理自动配置类

该配置方法是 Spring Boot 3 框架下的配置方法,若是 2 ,网上找找也有对应方法。

在项目的 resource 目录中添加 META-INF.spring 目录,并新建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports ,在文件中添加:

com.demo.common.security.config.WebConfig

即可默认配置该配置类,而不用在项目启动类中声明。

若不想全局配置,则在启动类中声明:

@SpringBootApplication(scanBasePackages = {"com.demo.common.security.config.WebConfig"})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeHTao_3381

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值