springmvcアプリケーション-カスタムパラメーターパーサー

この記事では主に、カスタムパラメーターパーサーを介してspringmvcの拡張ポイントを学習します。
実行したい主な操作は
、カスタムアノテーションを使用して、入力パラメーターのjson形式の文字列をカスタムパラメーターパーサーリスト配列を介して指定したタイプに変換することです。

応用

カスタムパラメータパーサー

public class MyHandlerMethodArgumentResolverTest implements HandlerMethodArgumentResolver {
    
    
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
    
    
        Json ann = (Json) methodParameter.getParameterAnnotation(Json.class);
        System.out.println(methodParameter);
        return ann != null;
    }

    @Override
    public 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());
    }
}

カスタムパラメータパーサーをスプリングコンテナに追加します

@Configuration
@ComponentScan("com.springmvc")
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
    
    

    @Override
    public 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)
@Documented
public @interface Json {
    
    
    String value() default "";

    Class<?> array();
}

コントローラインターフェース


@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":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{
    
    "imageType":4,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{
    
    "imageType":6,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{
    
    "imageType":1,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{
    
    "imageType":2,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"}]&haha=yesdy

説明する必要が
あります。ここで達成したい効果は次のとおりです。@ Jsonアノテーションが付けられたパラメーターの場合、文字列文字列を直接解析して、@ Jsonアノテーションで指定されたクラスタイプの配列にします。

ソースコード

春にカスタムパラメータパーサーを追加する

まず、カスタムパラメータパーサーをspringに与える方法
。springmvcプロジェクトの場合、springはrequestMappingHandlerAdapterオブジェクトを初期化します。これは、次のクラスの@Beanによって初期化されます。

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    
    
}

このメソッドには、カスタムパラメーターパーサーをコンテナーに追加することに関連するコード行があります。

adapter.setCustomArgumentResolvers(this.getArgumentResolvers());

ここでは、getArgumentResolversを呼び出して、すべてのパラメーターリゾルバーを取得します。このメソッドの処理ロジックでは、WebMvconfigurerのすべてのaddArgumentResolvers()メソッドが呼び出されます。したがって、WebMvcConfigurerを継承する実装クラスのaddArgumentResolvers()をオーバーライドするだけで済みます。次に、カスタムパラメーターパーサーをリゾルバーに追加します

WebMvcConfigurationSupportクラスは、@ EnableWebMvcアノテーションを追加すると、@ Importを介してこのクラスを継承する完全な構成クラスを挿入します。

この方法はあまり説明されません。デバッグはソースコードを見て、一目でわかります。複雑なロジックはありません。

カスタムパラメータパーサーの使用方法

パラメータパーサーをカスタマイズしてコンテナに配置すると、すべてのリクエストがorg.springframework.web.servlet.DispatcherServlet#doDispatchに対して実行されます。このメソッドでは、現在のメソッドで使用されているhandlerMappingとhandlerAdapterが解決されます。ターゲットメソッドが呼び出されます。ターゲットメソッドを
呼び出す前に、入力パラメータが1つずつ解析されます。
この記事では、パラメータ解析のロジックのみを考慮しているため、呼び出しチェーンは次のようになります。

org.springframework.web.servlet.DispatcherServlet#doDispatch
  org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleInternal
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
        org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
          org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
            org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

使用するパラメーターパーサーを決定する

パラメータ解析の中核はこれらのコード行にあります。このコードブロックの外層はforループであり、すべての引数をトラバースします。

argumentsResolvers.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
 */
@Nullable
private 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;
}

このメソッドは、最もコアなメソッドの1つです。パラメーターパーサーが現在のパラメーターを解析できるかどうかを判断します。
ここでは、カスタムを含むすべてのパラメーターパーサーを取得します。たとえば、カスタムパラメーターパーサー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は、このパラメーターで使用されるパラメーターパーサーを、パラメーターに従ってマップコレクションから取得し、それを解決
します。ここでのresolveArgumentは、カスタムパラメーターパーサーの解析メソッドを呼び出します。

つまり、アイデアは次のよう
になります。1。Springはパラメーターパーサーのインターフェイスを提供します。カスタマイズする場合は、インターフェイスを実装します
。2。次にカスタムパラメーターパーサーをSpringコンテナーに配置します。3
。メソッドが呼び出されたとき、Springは、Spring独自の+カスタムパラメーターパーサーを使用して
4をトラバースしますパラメーターで解析できるパーサーを判断した後、パラメーターを対応するパラメーターパーサーに渡して分析します。

おすすめ

転載: blog.csdn.net/CPLASF_/article/details/110234959