SpringBoot parameter alias implementation
first published on Dale's blog
Background and pain points
There is often a situation in the project: after a certain parameter is set, the front end asks to change the parameter name, and you don't want to change the elegance of the code because of this name, so you need to have the ability to have one parameter corresponding to two parameter names.
Specifically: the business has a custom protocol, and the name defined in the java entity is the full name, for example: name
getter and setter are getName()
and setName(String name)
respectively.
However, in the process of protocol forwarding, you need to use abbreviations to optimize the size of the message body {"nm":"Dale"}
. For unknown reasons, the front end requires the use of nm
corresponding to the original name
.
Ideas
Use the annotation to set the alias of the parameter, and bind the alias to the parameter name of the entity. Then use
ExtendedServletRequestDataBinder.addBindValues
re-correlate the value corresponding to the alias with the parameter name of the entity. Some mouthfuls, take the example in the background as an example: the received parameter is nm
, and the value is Dale
. After binding, nm
the value of name
to 0616edd7fc76fe.
Not much nonsense, just go to the code.
Code
ValueFrom
/**
* 请求参数别名注解
*
* @author Dale
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @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
Re-inject 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)));
}
}
Finally, use WebMvcConfigurer.addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
inject AliasModelAttributeMethodProcessor
WebMvcConfiguration
/**
* web mvc 配置
*
* @author Dale
*/
@Component
public 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);
}
}
use
Use annotations to set aliases when defining incoming parameters
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();
}
}
Summarize
The above is a SpringBoot
set the parameter alias, the depth is not high, find time to dig deeper.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。