100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > springmvc应用-自定义参数解析器

springmvc应用-自定义参数解析器

时间:2019-05-13 06:35:23

相关推荐

springmvc应用-自定义参数解析器

本文主要是通过自定义参数解析器学习springmvc的扩展点

主要想完成的操作是:

通过自定义一个注解,将入参中的 json格式的字符串,通过自定义的参数解析器转换为指定类型的list数组

应用

自定义参数解析器

public class MyHandlerMethodArgumentResolverTest implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Json ann = (Json) methodParameter.getParameterAnnotation(Json.class);System.out.println(methodParameter);return ann != null;}@Overridepublic Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {Json ann = (Json) methodParameter.getParameterAnnotation(Json.class);String param1 = ann.value();System.out.println(param1);System.out.println(ann.array());String parameter = nativeWebRequest.getParameter(param1);return JSON.parseArray(parameter, ann.array());}}

将自定义参数解析器加入到spring容器中

@Configuration@ComponentScan("com.springmvc")@EnableWebMvcpublic class AppConfig implements WebMvcConfigurer {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new MyHandlerMethodArgumentResolverTest());}// @Override// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {//converters.add(new MappingJackson2HttpMessageConverter());//converters.add(new StringHttpMessageConverter());// }}

自定义注解Json

@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Json {String value() default "";Class<?> array();}

controller接口

@RequestMapping("/testMyAnno")public String testMyAnno(@Json(value = "testList", array = ImageInfo.class) List<ImageInfo> testList,@RequestParam("haha") String userName) {System.out.println("list信息是:" + testList);System.out.println("userName是:" + userName);UserInfo userInfo = new UserInfo(userName, testList);System.out.println(userInfo.toString());return "success";}

请求url:

http://127.0.0.1:8080/testMyAnno.do?testList=[{"imageType":3,"imageUrl":"/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":4,"imageUrl":"/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":6,"imageUrl":"/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":1,"imageUrl":"/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":2,"imageUrl":"/40accdeff31be8bf84fbb69e632e37d9.png"}]&haha=yesdy

需要解释一下,我这里想要实现的效果就是:

对于加了@Json注解的参数,直接将其String字符串解析为@Json注解中指定的Class类型的数组

源码

将自定义参数解析器加入到spring中

首先来说如何将一个自定义的参数解析器交给spring

对于springmvc项目,spring会初始化requestMappingHandlerAdapter对象,就是在下面这个类中,通过@Bean初始化的

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter() {}

在这个方法中,有一行代码,和我们将自定义参数解析器加入到容器中有关系,就是

adapter.setCustomArgumentResolvers(this.getArgumentResolvers());

这里会调用getArgumentResolvers获取到所有的参数解析器,在这个方法的处理逻辑中,会调用所有WebMvconfigurer的addArgumentResolvers()方法,所以,我们只需要在继承了WebMvcConfigurer的实现类中,覆写addArgumentResolvers(),然后将自定义的参数解析器,添加到resolvers 中即可

WebMvcConfigurationSupport这个类是在我们添加@EnableWebMvc注解的时候,是会通过@Import注入一个继承了该类的全配置类

这个方法就不做过多的解释了,debug看下源码,就一目了然了,没有什么复杂的逻辑

如何使用自定义参数解析器

我们自定义了参数解析器,并且放入到了容器中,那所有的请求,都会执行到org.springframework.web.servlet.DispatcherServlet#doDispatch,在这个方法中,解析到当前方法所使用的handlerMapping和handlerAdapter之后,就会开始调用目标方法

在调用目标方法之前,会对入参一一进行解析

我们这篇文章,只关心参数解析的逻辑,所以调用链是这样的

org.springframework.web.servlet.DispatcherServlet#doDispatchorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleInternalorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethodorg.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandleorg.springframework.web.method.support.InvocableHandlerMethod#invokeForRequestorg.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

判断使用哪个参数解析器解析

对参数的解析,核心的就是在这几行代码中,在这个代码块外层是一个for循环,会遍历所有的args

argumentResolvers.supportsParameter(parameter):这行代码中,会拿着所有的参数解析器,去对当前参数args[i],进行判断,看哪个参数解析器可以解析该参数

/*** 遍历所有的参数解析器,看哪个解析器可以解析,如果参数解析器返回true,就表示可以解析* 如果我们要扩展参数解析器,就需要看下这里的逻辑* 需要学习下这里的argumentResolvers是在哪里赋值的*/if (this.argumentResolvers.supportsParameter(parameter)) {try {/*** 这里就是用parameter对应的解析器去解析该参数*/args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}}

/*** 遍历所有的参数处理器,找到处理该parameter的处理器,然后存入到map集合中,* 第二次获取处理该参数的处理器时,就无须再次遍历所有的support方法,直接从map缓存中获取即可* @param parameter* @return*/@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {if (logger.isTraceEnabled()) {logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +parameter.getGenericParameterType() + "]");}/*** 核心方法就是这里,上面会遍历所有的参数解析器,依次去调用对应的supports方法,判断是否可以处理parameter* 如果可以,就返回true,然后会把对应的methodArgumentResolver和对应的parameter关联起来*/if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;}

这个方法就是最为核心的方法之一:判断参数解析器是否可以解析当前参数;

这里会获取到所有的参数解析器,包括我们自定义的,举例:我们自定义的参数解析器A支持解析args[i]之后,那就会把A放入到argumentResolverCache这个缓存中

这里的思想就是:我们只需要提供参数解析器,并且指定我们自定义的参数解析器要解析哪种参数,spring自己会获取到所有的参数解析器,去和要解析的参数进行匹配,这种设计模式应该是模板设计模式,总之思想就是这样的

使用对应的参数解析器解析参数

下一个比较核心的方法就是去解析的逻辑

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {/*** 这个方法是实际解析参数的逻辑,首先会先获取到参数对应的解析器*/HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");}/*** 这里调用的就是处理的方法*/return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}

可以看到,spring会从map集合中根据parameter,获取这个参数所使用的参数解析器,然后去解析

这里的resolveArgument就会去调用到自定义参数解析器中的解析方法

总之:思想就是这样的

1、spring提供了参数解析器的接口,如果我们要自定义,就实现该接口

2、然后把自定义的参数解析器放到spring容器中

3、在方法被调用的时候,spring会拿着spring自己的 + 我们自定义的参数解析器去遍历

4、判断到参数可以被哪个解析器解析之后,就会把该参数交给对应的参数解析器去解析

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。