SpringMVC提供了各類姿式的http參數解析支持,從前面的GET/POST參數解析篇也能夠看到,加一個@RequsetParam
註解就能夠將方法參數與http參數綁定,看到這時天然就會好奇這是怎麼作到的,咱們能不能本身定義一種參數解析規則呢?java
本文將介紹如何實現自定義的參數解析,並讓其生效git
<!-- more -->github
首先得搭建一個web應用纔有可能繼續後續的測試,藉助SpringBoot搭建一個web應用屬於比較簡單的活;web
建立一個maven項目,pom文件以下spring
<parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>2.1.7</version> <relativepath /> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceencoding>UTF-8</project.build.sourceencoding> <project.reporting.outputencoding>UTF-8</project.reporting.outputencoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> </dependencies> <build> <pluginmanagement> <plugins> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> </plugin> </plugins> </pluginmanagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
對於如何自定義參數解析器,一個較推薦的方法是,先搞清楚springmvc接收到一個請求以後完整的處理鏈路,而後再來看在什麼地方,什麼時機,來插入自定義參數解析器,不管是從理解仍是實現都會簡單不少。遺憾的是,本篇主要目標放在的是使用角度,因此這裏只會簡單的提一下參數解析的鏈路,具體的深刻留待後續的源碼解析數組
http請求流程圖,來自 SpringBoot是如何解析HTTP參數的mvc
既然是參數解析,因此確定是在方法調用以前就會被觸發,在Spring中,負責將http參數與目標方法參數進行關聯的,主要是藉助org.springframework.web.method.support.HandlerMethodArgumentResolver
類來實現app
/** * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found. */ @Override @Nullable 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); }
上面這段核心代碼來自org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
,主要做用就是獲取一個合適的HandlerMethodArgumentResolver
,實現將http參數(webRequest
)映射到目標方法的參數上(parameter
)maven
因此說,實現自定義參數解析器的核心就是實現一個本身的HandlerMethodArgumentResolver
ide
實現一個自定義的參數解析器,首先得有個目標,咱們在get參數解析篇裏面,當時遇到了一個問題,當傳參爲數組時,定義的方法參數須要爲數組,而不能是List,不然沒法正常解析;如今咱們則但願能實現這樣一個參數解析,以支持上面的場景
爲了實現上面這個小目標,咱們能夠以下操做
定義這個註解,主要就是用於代表,帶有這個註解的參數,但願可使用咱們自定義的參數解析器來解析;
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ListParam { /** * Alias for {@link #name}. */ @AliasFor("name") String value() default ""; /** * The name of the request parameter to bind to. * * @since 4.2 */ @AliasFor("value") String name() default ""; }
接下來就是自定義的參數解析器了,須要實現接口HandlerMethodArgumentResolver
public class ListHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(ListParam.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { ListParam param = parameter.getParameterAnnotation(ListParam.class); if (param == null) { throw new IllegalArgumentException( "Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } String name = "".equalsIgnoreCase(param.name()) ? param.value() : param.name(); if ("".equalsIgnoreCase(name)) { name = parameter.getParameter().getName(); } String ans = webRequest.getParameter(name); if (ans == null) { return null; } String[] cells = StringUtils.split(ans, ","); return Arrays.asList(cells); } }
上面有兩個方法:
supportsParameter
就是用來代表這個參數解析器適不適用
ListParam
註解resolveArgument
這個方法就是實現將http參數粗轉換爲目標方法參數的具體邏輯
List<string>
上面雖然實現了自定義的參數解析器,可是咱們須要把它註冊到HandlerMethodArgumentResolver
才能生效,一個簡單的方法以下
@SpringBootApplication public class Application extends WebMvcConfigurationSupport { @Override protected void addArgumentResolvers(List<handlermethodargumentresolver> argumentResolvers) { argumentResolvers.add(new ListHandlerMethodArgumentResolver()); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
爲了驗證咱們的自定義參數解析器ok,咱們開兩個對比的rest服務
@RestController @RequestMapping(path = "get") public class ParamGetRest { /** * 自定義參數解析器 * * @param names * @param age * @return */ @GetMapping(path = "self") public String selfParam(@ListParam(name = "names") List<string> names, Integer age) { return names + " | age=" + age; } @GetMapping(path = "self2") public String selfParam2(List<string> names, Integer age) { return names + " | age=" + age; } }
演示demo以下,添加了ListParam
註解的能夠正常解析,沒有添加註解的會拋異常
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
</string></string></handlermethodargumentresolver></string>