最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解如何在SpringBoot中自定義參數(shù)解析器

 更新時間:2022年05月11日 10:48:23   作者:江南一點雨  
這篇文章主要為大家詳細介紹了如何在SpringBoot中實現(xiàn)自定義參數(shù)解析器,文中的示例代碼講解詳細,對我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下

前言

在一個 Web 請求中,參數(shù)我們無非就是放在地址欄或者請求體中,個別請求可能放在請求頭中。

放在地址欄中,我們可以通過如下方式獲取參數(shù):

String?javaboy?=?request.getParameter("name?");

放在請求體中,如果是 key/value 形式,我們可以通過如下方式獲取參數(shù):

String?javaboy?=?request.getParameter("name?");

如果是 JSON 形式,我們則通過如果如下方式獲取到輸入流,然后解析成 JSON 字符串,再通過 JSON 工具轉(zhuǎn)為對象:

BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(request.getInputStream()));
String?json?=?reader.readLine();
reader.close();
User?user?=?new?ObjectMapper().readValue(json,?User.class);

如果參數(shù)放在請求頭中,我們可以通過如下方式獲取:

String?javaboy?=?request.getHeader("name");

如果你用的是 Jsp/Servlet 那一套技術(shù)棧,那么參數(shù)獲取無外乎這幾種方式。

如果用了 SpringMVC 框架,有的小伙伴們可能會覺得參數(shù)獲取方式太豐富了,各種注解如 @RequestParam、@RequestBody、@RequestHeader@PathVariable,參數(shù)可以是 key/value 形式,也可以是 JSON 形式,非常豐富!但是,無論多么豐富,最底層獲取參數(shù)的方式無外乎上面幾種。

那有小伙伴要問了,SpringMVC 到底是怎么樣從 request 中把參數(shù)提取出來直接給我們用的呢?例如下面這個接口:

@RestController
public?class?HelloController?{
????@GetMapping("/hello")
????public?String?hello(String?name)?{
????????return?"hello?"+name;
????}
}

我們都知道 name 參數(shù)是從 HttpServletRequest 中提取出來的,到底是怎么提取出來的?這就是松哥今天要和大家分享的話題。

1.自定義參數(shù)解析器

為了搞清楚這個問題,我們先來自定義一個參數(shù)解析器看看。

自定義參數(shù)解析器需要實現(xiàn) HandlerMethodArgumentResolver 接口,我們先來看看該接口:

public?interface?HandlerMethodArgumentResolver?{
?boolean?supportsParameter(MethodParameter?parameter);
?@Nullable
?Object?resolveArgument(MethodParameter?parameter,?@Nullable?ModelAndViewContainer?mavContainer,
???NativeWebRequest?webRequest,?@Nullable?WebDataBinderFactory?binderFactory)?throws?Exception;

}

這個接口中就兩個方法:

  • supportsParameter:該方法表示是否啟用這個參數(shù)解析器,返回 true 表示啟用,返回 false 表示不啟用。
  • resolveArgument:這是具體的解析過程,就是從 request 中取出參數(shù)的過程,方法的返回值就對應(yīng)了接口中參數(shù)的值。

自定義參數(shù)解析器只需要實現(xiàn)該接口即可。

假設(shè)我現(xiàn)在有這樣一個需求(實際上在 Spring Security 中獲取當(dāng)前登錄用戶名非常方便,這里只是為了該案例而做,勿抬杠):

假設(shè)我現(xiàn)在系統(tǒng)安全框架使用了 Spring Security,如果我在接口的參數(shù)上添加了 @CurrentUserName 注解,那么該參數(shù)的值就是當(dāng)前登錄的用戶名,像下面這樣:

@RestController
public?class?HelloController?{
????@GetMapping("/hello")
????public?String?hello(@CurrentUserName?String?name)?{
????????return?"hello?"+name;
????}
}

要實現(xiàn)這個功能,非常 easy,首先我們自定義一個 @CurrentUserName 注解,如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public?@interface?CurrentUserName?{
}

這個注解沒啥好解釋的。

接下來我們自定義參數(shù)解析器 CurrentUserNameHandlerMethodArgumentResolver,如下:

public?class?CurrentUserNameHandlerMethodArgumentResolver?implements?HandlerMethodArgumentResolver?{
????@Override
????public?boolean?supportsParameter(MethodParameter?parameter)?{
????????return?parameter.getParameterType().isAssignableFrom(String.class)&&parameter.hasParameterAnnotation(CurrentUserName.class);
????}

????@Override
????public?Object?resolveArgument(MethodParameter?parameter,?ModelAndViewContainer?mavContainer,?NativeWebRequest?webRequest,?WebDataBinderFactory?binderFactory)?throws?Exception?{
????????User?user?=?(User)?SecurityContextHolder.getContext().getAuthentication().getPrincipal();
????????return?user.getUsername();
????}
}
  • supportsParameter:如果參數(shù)類型是 String,并且參數(shù)上有 @CurrentUserName 注解,則使用該參數(shù)解析器。
  • resolveArgument:該方法的返回值就是參數(shù)的具體值,當(dāng)前登錄用戶名從 SecurityContextHolder 中獲取即可

最后,我們再將自定義的參數(shù)解析器配置到 HandlerAdapter 中,配置方式如下:

@Configuration
public?class?WebConfig?implements?WebMvcConfigurer?{
????@Override
????public?void?addArgumentResolvers(List<HandlerMethodArgumentResolver>?resolvers)?{
????????resolvers.add(new?CurrentUserNameHandlerMethodArgumentResolver());
????}
}

至此,就算配置完成了。

接下來啟動項目,用戶登錄成功后,訪問 /hello 接口,就可以看到返回當(dāng)前登錄用戶數(shù)據(jù)了。

這就是我們自定義的一個參數(shù)類型解析器。可以看到,非常 Easy。

在 SpringMVC 中,默認也有很多 HandlerMethodArgumentResolver 的實現(xiàn)類,他們處理的問題也都類似,松哥再給大家舉個例子。

2.PrincipalMethodArgumentResolver

如果我們在項目中使用了 Spring Security,我們可以通過如下方式獲取當(dāng)前登錄用戶信息:

@GetMapping("/hello2")
public?String?hello2(Principal?principal)?{
????return?"hello?"?+?principal.getName();
}

即直接在當(dāng)前接口的參數(shù)中添加 Principal 類型的參數(shù)即可,該參數(shù)描述了當(dāng)前登錄用戶信息,這個用過 Spring Security 的小伙伴應(yīng)該都知道

那么這個功能是怎么實現(xiàn)的呢?當(dāng)然就是 PrincipalMethodArgumentResolver 在起作用了!

我們一起來看下這個參數(shù)解析器:

public?class?PrincipalMethodArgumentResolver?implements?HandlerMethodArgumentResolver?{

?@Override
?public?boolean?supportsParameter(MethodParameter?parameter)?{
??return?Principal.class.isAssignableFrom(parameter.getParameterType());
?}

?@Override
?public?Object?resolveArgument(MethodParameter?parameter,?@Nullable?ModelAndViewContainer?mavContainer,
???NativeWebRequest?webRequest,?@Nullable?WebDataBinderFactory?binderFactory)?throws?Exception?{

??HttpServletRequest?request?=?webRequest.getNativeRequest(HttpServletRequest.class);
??if?(request?==?null)?{
???throw?new?IllegalStateException("Current?request?is?not?of?type?HttpServletRequest:?"?+?webRequest);
??}

??Principal?principal?=?request.getUserPrincipal();
??if?(principal?!=?null?&&?!parameter.getParameterType().isInstance(principal))?{
???throw?new?IllegalStateException("Current?user?principal?is?not?of?type?["?+
?????parameter.getParameterType().getName()?+?"]:?"?+?principal);
??}

??return?principal;
?}

}
  • supportsParameter:這個方法主要是判斷參數(shù)類型是不是 Principal,如果參數(shù)類型是 Principal,就支持。
  • resolveArgument:這個方法的邏輯很簡單,首先獲取原生的請求,再從請求中獲取 Principal 對象返回即可。

是不是很簡單,有了這個,我們就可以隨時加載到當(dāng)前登錄用戶信息了。

3.RequestParamMapMethodArgumentResolver

松哥再給大家舉個例子:

@RestController
public?class?HelloController?{
????@PostMapping("/hello")
????public?void?hello(@RequestParam?MultiValueMap?map)?throws?IOException?{
????????//省略...
????}
}

這個接口很多小伙伴可能都寫過,使用 Map 去接收前端傳來的參數(shù),那么這里用到的參數(shù)解析器就是 RequestParamMapMethodArgumentResolver。

public?class?RequestParamMapMethodArgumentResolver?implements?HandlerMethodArgumentResolver?{

?@Override
?public?boolean?supportsParameter(MethodParameter?parameter)?{
??RequestParam?requestParam?=?parameter.getParameterAnnotation(RequestParam.class);
??return?(requestParam?!=?null?&&?Map.class.isAssignableFrom(parameter.getParameterType())?&&
????!StringUtils.hasText(requestParam.name()));
?}

?@Override
?public?Object?resolveArgument(MethodParameter?parameter,?@Nullable?ModelAndViewContainer?mavContainer,
???NativeWebRequest?webRequest,?@Nullable?WebDataBinderFactory?binderFactory)?throws?Exception?{

??ResolvableType?resolvableType?=?ResolvableType.forMethodParameter(parameter);

??if?(MultiValueMap.class.isAssignableFrom(parameter.getParameterType()))?{
???//?MultiValueMap
???Class<?>?valueType?=?resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
???if?(valueType?==?MultipartFile.class)?{
????MultipartRequest?multipartRequest?=?MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
????return?(multipartRequest?!=?null???multipartRequest.getMultiFileMap()?:?new?LinkedMultiValueMap<>(0));
???}
???else?if?(valueType?==?Part.class)?{
????HttpServletRequest?servletRequest?=?webRequest.getNativeRequest(HttpServletRequest.class);
????if?(servletRequest?!=?null?&&?MultipartResolutionDelegate.isMultipartRequest(servletRequest))?{
?????Collection<Part>?parts?=?servletRequest.getParts();
?????LinkedMultiValueMap<String,?Part>?result?=?new?LinkedMultiValueMap<>(parts.size());
?????for?(Part?part?:?parts)?{
??????result.add(part.getName(),?part);
?????}
?????return?result;
????}
????return?new?LinkedMultiValueMap<>(0);
???}
???else?{
????Map<String,?String[]>?parameterMap?=?webRequest.getParameterMap();
????MultiValueMap<String,?String>?result?=?new?LinkedMultiValueMap<>(parameterMap.size());
????parameterMap.forEach((key,?values)?->?{
?????for?(String?value?:?values)?{
??????result.add(key,?value);
?????}
????});
????return?result;
???}
??}

??else?{
???//?Regular?Map
???Class<?>?valueType?=?resolvableType.asMap().getGeneric(1).resolve();
???if?(valueType?==?MultipartFile.class)?{
????MultipartRequest?multipartRequest?=?MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
????return?(multipartRequest?!=?null???multipartRequest.getFileMap()?:?new?LinkedHashMap<>(0));
???}
???else?if?(valueType?==?Part.class)?{
????HttpServletRequest?servletRequest?=?webRequest.getNativeRequest(HttpServletRequest.class);
????if?(servletRequest?!=?null?&&?MultipartResolutionDelegate.isMultipartRequest(servletRequest))?{
?????Collection<Part>?parts?=?servletRequest.getParts();
?????LinkedHashMap<String,?Part>?result?=?CollectionUtils.newLinkedHashMap(parts.size());
?????for?(Part?part?:?parts)?{
??????if?(!result.containsKey(part.getName()))?{
???????result.put(part.getName(),?part);
??????}
?????}
?????return?result;
????}
????return?new?LinkedHashMap<>(0);
???}
???else?{
????Map<String,?String[]>?parameterMap?=?webRequest.getParameterMap();
????Map<String,?String>?result?=?CollectionUtils.newLinkedHashMap(parameterMap.size());
????parameterMap.forEach((key,?values)?->?{
?????if?(values.length?>?0)?{
??????result.put(key,?values[0]);
?????}
????});
????return?result;
???}
??}
?}

}
  • supportsParameter:參數(shù)類型是 Map,并且使用了 @RequestParam 注解,并且 @RequestParam 注解中沒有配置 name 屬性,就可以使用該參數(shù)解析器。
  • resolveArgument:具體解析分為兩種情況:MultiValueMap 和其他 Map,前者中又分三種情況:MultipartFile、Part 或者其他普通請求,前兩者可以處理文件上傳,第三個就是普通參數(shù)。如果是普通 Map,則直接獲取到原始請求參數(shù)放到一個 Map 集合中返回即可。

4.小結(jié)

前面和大家聊的都是幾種簡單的情況,還有復(fù)雜的如 PathVariableMethodArgumentResolver 和 RequestParamMethodArgumentResolver 松哥以后再和大家詳細聊。

以上就是詳解如何在SpringBoot中自定義參數(shù)解析器的詳細內(nèi)容,更多關(guān)于SpringBoot自定義參數(shù)解析器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 希爾排序的算法代碼

    希爾排序的算法代碼

    希爾排序也是一種插入排序方法,實際上是一種分組插入方法。
    2013-03-03
  • Java如何判斷線程是否結(jié)束的三種方法

    Java如何判斷線程是否結(jié)束的三種方法

    本文主要介紹了Java如何判斷線程是否結(jié)束的三種方法,主要介紹了三種方法,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 合成聚合復(fù)用原則_動力節(jié)點Java學(xué)院整理

    合成聚合復(fù)用原則_動力節(jié)點Java學(xué)院整理

    這篇文章主要介紹了合成聚合復(fù)用原則,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • SpringBoot FreeWorker模板技術(shù)解析

    SpringBoot FreeWorker模板技術(shù)解析

    這篇文章主要介紹了SpringBoot FreeWorker模板技術(shù)解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Spring?Cloud?OpenFeign實例介紹使用方法

    Spring?Cloud?OpenFeign實例介紹使用方法

    Spring?Cloud?OpenFeign?對?Feign?進行了二次封裝,使得在?Spring?Cloud?中使用?Feign?的時候,可以做到使用?HTTP?請求訪問遠程服務(wù),就像調(diào)用本地方法一樣的,開發(fā)者完全感知不到這是在調(diào)用遠程訪問,更感知不到在訪問?HTTP?請求
    2022-09-09
  • 詳解Java中如何正確書寫單例模式

    詳解Java中如何正確書寫單例模式

    一般單例都是五種寫法:懶漢,餓漢,雙重校驗鎖,靜態(tài)內(nèi)部類和枚舉。本文整理了幾種常見的單例寫法,下面跟著小編一起來看下吧
    2017-01-01
  • SpringBoot如何實現(xiàn)starter原理詳解

    SpringBoot如何實現(xiàn)starter原理詳解

    這篇文章主要介紹了SpringBoot如何實現(xiàn)starter原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 基于Hutool的圖片驗證碼功能模塊實現(xiàn)

    基于Hutool的圖片驗證碼功能模塊實現(xiàn)

    為了提高系統(tǒng)的安全性,防止接口被暴力刷新,驗證碼是個好的手段,圖片驗證碼沒有短信驗證碼的費用,其是個人開發(fā)者學(xué)習(xí)的重點,這篇文章主要介紹了基于Hutool的圖片驗證碼功能模塊實現(xiàn),需要的朋友可以參考下
    2022-10-10
  • idea中MavenWeb項目不能創(chuàng)建Servlet的解決方案

    idea中MavenWeb項目不能創(chuàng)建Servlet的解決方案

    這篇文章主要介紹了idea中MavenWeb項目不能創(chuàng)建Servlet的解決方案,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • 詳解spring boot集成RabbitMQ

    詳解spring boot集成RabbitMQ

    RabbitMQ作為AMQP的代表性產(chǎn)品,在項目中大量使用。結(jié)合現(xiàn)在主流的spring boot,極大簡化了開發(fā)過程中所涉及到的消息通信問題。
    2017-03-03

最新評論

马尔康县| 房产| 通化市| 家居| 太仓市| 麟游县| 左权县| 益阳市| 雅江县| 太原市| 乌拉特前旗| 永济市| 樟树市| 漳州市| 蚌埠市| 淮南市| 山阴县| 台中市| 理塘县| 浏阳市| 双桥区| 绿春县| 重庆市| 湖北省| 如东县| 嘉义市| 竹山县| 遂昌县| 玛纳斯县| 定日县| 仙游县| 麻阳| 云龙县| 冕宁县| 永定县| 万山特区| 安阳县| 高淳县| 嘉黎县| 松江区| 湛江市|