この記事では主に、カスタムパラメーターパーサーを介して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をトラバースします。パラメーターで解析できるパーサーを判断した後、パラメーターを対応するパラメーターパーサーに渡して分析します。