环境: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类型的都能进行转换protected boolean supports(Class<?> clazz) {return Users.class == clazz ;}// 读取内容进行内容的转换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方法。protected void writeInternal(Object t, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {outputMessage.getBody().write(t.toString().getBytes()) ;}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;}public boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(Pack.class) ;}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 T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableExceptionreturn readInternal(clazz, inputMessage);}
还是调用子类的readInternal方法。
这里的消息转换器我是直接用的,如果你源码调试过你会知道,有些消息转换器内部是有个HttpMessageConverter List的,依次判断当前转换器是否支持。这里为了简单直接使用了。
自定义注解类
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface Pack {}
配置消息转换器及参数解析器
public class WebConfig implements WebMvcConfigurer {public CustomHttpMessageConverter customHttpMessageConverter() {return new CustomHttpMessageConverter() ;}public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {CustomHttpMessageConverter messageConvert = customHttpMessageConverter() ;List<MediaType> supportedMediaTypes = new ArrayList<>() ;// 接口请求的时候需要设置contentType=application/fmsupportedMediaTypes.add(new MediaType("application", "fm")) ;messageConvert.setSupportedMediaTypes(supportedMediaTypes) ;converters.add(messageConvert) ;}public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {CustomHandlerMethodParameterResolver customResolver = new CustomHandlerMethodParameterResolver() ;customResolver.setMessageConverter(customHttpMessageConverter()) ;resolvers.add(customResolver) ;}}
Controller接口
public Object resolver( Users user) {System.out.println("自定义参数解析器处理结果:" + user) ;return user ;}
测试


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



