SpringBoot 参数别名实现

首发于 Dale‘s blog

背景与痛点

我的项目中经常出现一种状况:定了某个参数之后,前端又要求改参数名字,而你又不想因为这个名字而扭转代码的优雅,于是就须要领有一个参数对应两个参数名的能力。
具体来说:业务领有自定义协定,在java实体中定义的名字是全名,例如:name getter、setter 别离为 getName()setName(String name)
然而,在协定转发的过程中须要应用简写来优化音讯体的大小{"nm":"Dale"},不晓得何种起因,前端要求应用nm对应原有的name

思路

应用注解设置参数的别名,将别名与实体的参数名绑定。而后再利用ExtendedServletRequestDataBinder.addBindValues从新将别名对应的值与实体的参数名对应。有些拗口,以背景中的例子为例:接管到的参数为nm,值为Dale。通过绑定之后,将nm的值绑定到name上。

废话不多,间接上代码。

代码

ValueFrom

/** * 申请参数别名注解 * * @author Dale */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ValueFrom {    /**     * 参数别名列表     */    String[] value();}

AliasDataBinder

/** * 别名数据绑定 * * @author Dale */public class AliasDataBinder extends ExtendedServletRequestDataBinder {    public AliasDataBinder(Object target, String objectName) {        super(target, objectName);    }    @Override    protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {        super.addBindValues(mpvs, request);        Class<?> targetClass = Objects.requireNonNull(getTarget()).getClass();        Class<?> targetFatherClass = targetClass.getSuperclass();        // 利用反射获取类的字段        Field[] fields = targetClass.getDeclaredFields();        Field[] superFields = targetFatherClass.getDeclaredFields();        for (Field field : fields) {            ValueFrom valueFromAnnotation = field.getAnnotation(ValueFrom.class);            if (mpvs.contains(field.getName()) || valueFromAnnotation == null) {                continue;            }            for (String alias : valueFromAnnotation.value()) {                if (mpvs.contains(alias)) {                    mpvs.add(field.getName(), Objects.requireNonNull(mpvs.getPropertyValue(alias)).getValue());                    break;                }            }        }        // 将参数绑定到父类上        for (Field field : superFields) {            ValueFrom valueFromAnnotation = field.getAnnotation(ValueFrom.class);            if (mpvs.contains(field.getName()) || valueFromAnnotation == null) {                continue;            }            for (String alias : valueFromAnnotation.value()) {                if (mpvs.contains(alias)) {                    mpvs.add(field.getName(), Objects.requireNonNull(mpvs.getPropertyValue(alias)).getValue());                    break;                }            }        }    }}

AliasModelAttributeMethodProcessor

从新注入DataBinder

/** * 参数别名绑定processor * * @author Dale */public class AliasModelAttributeMethodProcessor extends ServletModelAttributeMethodProcessor {    private ApplicationContext applicationContext;    public AliasModelAttributeMethodProcessor(boolean annotationNotRequired) {        super(annotationNotRequired);    }    public void setApplicationContext(ApplicationContext applicationContext){        this.applicationContext = applicationContext;    }    @Override    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {        // 从新注入 databinder        AliasDataBinder aliasDataBinder = new AliasDataBinder(binder.getTarget(), binder.getObjectName());        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);        Objects.requireNonNull(requestMappingHandlerAdapter.getWebBindingInitializer()).initBinder(aliasDataBinder);        aliasDataBinder.bind(Objects.requireNonNull(request.getNativeRequest(ServletRequest.class)));    }}

最初利用WebMvcConfigurer.addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)注入AliasModelAttributeMethodProcessor

WebMvcConfiguration

/** * web mvc 配置 * * @author Dale */@Componentpublic class WebMvcConfiguration implements WebMvcConfigurer {    @Override    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {        AliasModelAttributeMethodProcessor aliasModelAttributeMethodProcessor = new AliasModelAttributeMethodProcessor(true);        aliasModelAttributeMethodProcessor.setApplicationContext(applicationContext);        // 注入 AliasModelAttributeMethodProcessor        resolvers.add(aliasModelAttributeMethodProcessor);        WebMvcConfigurer.super.addArgumentResolvers(resolvers);    }}

应用

在定义传入参数的时候应用注解设置别名

RequestParam

/** * 发送音讯必要参数 * * @author baoxulong */public class RequestParam {    /**     * name     */    @NotNull(message = "name is required!")    @ValueFrom(value = "nm")    private String name;        public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    }

NameController

/** * name 控制器 * * @author Dale */@RestController@RequestMapping("name")public class NameController {    @PostMapping("set")    public JsonResult set(@Valid RequestParam requestParam) {        // todo::        return JsonResult.success();    }    }

总结

以上是 SpringBoot 设置参数别名的办法代码实记,深度不高,找工夫再深挖。