大数跨境
0
0

【进阶】SpringMVC自定义参数解析器:定制化参数处理的最佳实践

【进阶】SpringMVC自定义参数解析器:定制化参数处理的最佳实践 Spring全家桶实战案例
2023-11-28
0
导读:SpringMVC自定义参数解析器:定制化参数处理的最佳实践

环境:Springboot2.7.12


1. 概述

Spring自定义参数解析器是Spring框架中一个非常强大的功能,它允许开发者自定义参数解析逻辑,以满足特定的业务需求。通过自定义参数解析器,开发者可以解析非标准的参数类型、处理复杂的参数结构、提取特定格式的参数值等。

在SpringMVC框架中,参数解析器被定义为一种独立的组件,可以很方便地与其他模块集成。它提供了一种标准的接口,开发者可以通过实现这个接口来定义自己的参数解析逻辑。当请求进入Spring MVC控制器方法时,Spring会自动遍历所有的参数解析器,并执行相应的解析操作。

通过自定义参数解析器,开发者可以更加灵活地处理请求中的参数,提高应用程序的适应性和可扩展性。同时,它也使得代码更加模块化和可维护,减少了重复工作和出错的可能性。

2. 自定义解析器

现在需要处理入参是如下的消息格式

入参(Post body):

name:张三,age:20

自定义消息转换器

public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private static Logger logger = LoggerFactory.getLogger(CustomHttpMessageConverter.class) ;
// 这里指明了只要接收参数是Users类型的都能进行转换 @Override protected boolean supports(Class<?> clazz) { return Users.class == clazz ; }
// 读取内容进行内容的转换 @Override protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { String content = inToString(inputMessage.getBody()) ; String[] keys = content.split(",") ; Users instance = null ; try { instance = (Users) clazz.newInstance(); } catch (Exception e1) { e1.printStackTrace() ; } for (String key : keys) { String[] vk = key.split(":") ; try { Field[] fields = clazz.getDeclaredFields() ; for (Field f:fields) { if (f.getName().equals(vk[0])) { f.setAccessible(true) ; Class<?> type = f.getType() ; if (String.class == type) { f.set(instance, vk[1]) ; } else if (Integer.class == type) { f.set(instance, Integer.parseInt(vk[1])) ; } break ; } } } catch (Exception e) { logger.error("错误:{}", e) ; } } return instance ; }
// 如果将返回值以什么形式输出,这里就是调用了对象的toString方法。 @Override protected void writeInternal(Object t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { outputMessage.getBody().write(t.toString().getBytes()) ; }
@Override protected boolean canWrite(MediaType mediaType) { if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.isCompatibleWith(mediaType)) { return true; } } return false; }
private String inToString(InputStream is) { byte[] buf = new byte[10 * 1024] ; int leng = -1 ; StringBuilder sb = new StringBuilder() ; try { while ((leng = is.read(buf)) != -1) { sb.append(new String(buf, 0, leng)) ; } return sb.toString() ; } catch (IOException e) { throw new RuntimeException(e) ; } }
}


参数解析器
这个接口参数谁能处理,如何处理的问题。

public class CustomHandlerMethodParameterResolver implements HandlerMethodArgumentResolver {
private CustomHttpMessageConverter messageConverter ;
public CustomHttpMessageConverter getMessageConverter() { return messageConverter; }
public void setMessageConverter(CustomHttpMessageConverter messageConverter) { this.messageConverter = messageConverter; }
@Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(Pack.class) ; }
@Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
return messageConverter.read((Class<?>)parameter.getNestedGenericParameterType(), inputMessage) ; }
}

说明:
supportsParameter:方法确定了当前解析器能够处理什么样的参数,这里我自定义了一个Pack注解类,也就是在接口参数上添加@Pack注解。
resolveArgument:方法是对参数的解析的具体实现。这里就是调用我们的消息转换器来实现的。

注意:这里messageConverter.read 这是父类中的方法,如下:

@Overridepublic final T read(Class<? extends TclazzHttpInputMessage inputMessage) throws IOExceptionHttpMessageNotReadableException   return readInternal(clazz, inputMessage);}

还是调用子类的readInternal方法。

这里的消息转换器我是直接用的,如果你源码调试过你会知道,有些消息转换器内部是有个HttpMessageConverter List的,依次判断当前转换器是否支持。这里为了简单直接使用了。

自定义注解类

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface Pack {}


配置消息转换器及参数解析器

@Configurationpublic class WebConfig implements WebMvcConfigurer {
@Bean public CustomHttpMessageConverter customHttpMessageConverter() { return new CustomHttpMessageConverter() ;  } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { CustomHttpMessageConverter messageConvert = customHttpMessageConverter() ; List<MediaType> supportedMediaTypes = new ArrayList<>() ; // 接口请求的时候需要设置contentType=application/fm supportedMediaTypes.add(new MediaType("application", "fm")) ; messageConvert.setSupportedMediaTypes(supportedMediaTypes) ;    converters.add(messageConvert) ;  } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { CustomHandlerMethodParameterResolver customResolver = new CustomHandlerMethodParameterResolver() ; customResolver.setMessageConverter(customHttpMessageConverter()) ;    resolvers.add(customResolver) ;  }}

Controller接口

@PostMapping("/resolver")public Object resolver(@Pack Users user) {  System.out.println("自定义参数解析器处理结果:" + user) ;  return user ;}

测试

总之,Spring自定义参数解析器是一个非常强大的功能,它使得开发者可以更加灵活地处理请求中的参数,提高应用程序的适应性和可扩展性。通过实现特定的接口、定义解析逻辑、注册到Spring容器等步骤,开发者可以轻松地自定义参数解析器,以满足特定的业务需求。使用自定义参数解析器可以减少代码重复、提高代码可读性和可维护性,是开发中不可或缺的一部分。

完毕!!!

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读7
粉丝0
内容832