一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務(wù)器之家 - 編程語言 - JAVA教程 - SpringBoot 中如何自定義參數(shù)解析器?

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

2021-03-17 01:03江南一點(diǎn)雨 JAVA教程

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. @RestController 
  2. public class HelloController { 
  3.     @GetMapping("/hello"
  4.     public String hello(String name) { 
  5.         return "hello "+name
  6.     } 

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

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

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

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

  1. public interface HandlerMethodArgumentResolver { 
  2.  boolean supportsParameter(MethodParameter parameter); 
  3.  @Nullable 
  4.  Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  5.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; 
  6.  

這個(gè)接口中就兩個(gè)方法:

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

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

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

假設(shè)我現(xiàn)在系統(tǒng)安全框架使用了 Spring Security(對 Spring Security 不熟悉的小伙伴,可以在公眾號江南一點(diǎn)雨后臺回復(fù) ss,有教程),如果我在接口的參數(shù)上添加了 @CurrentUserName 注解,那么該參數(shù)的值就是當(dāng)前登錄的用戶名,像下面這樣:

  1. @RestController 
  2. public class HelloController { 
  3.     @GetMapping("/hello"
  4.     public String hello(@CurrentUserName String name) { 
  5.         return "hello "+name
  6.     } 

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

  1. @Retention(RetentionPolicy.RUNTIME) 
  2. @Target(ElementType.PARAMETER) 
  3. public @interface CurrentUserName { 

這個(gè)注解沒啥好解釋的。

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

  1. public class CurrentUserNameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.     @Override 
  3.     public boolean supportsParameter(MethodParameter parameter) { 
  4.         return parameter.getParameterType().isAssignableFrom(String.class)&&parameter.hasParameterAnnotation(CurrentUserName.class); 
  5.     } 
  6.  
  7.     @Override 
  8.     public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
  9.         User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
  10.         return user.getUsername(); 
  11.     } 

supportsParameter:如果參數(shù)類型是 String,并且參數(shù)上有 @CurrentUserName 注解,則使用該參數(shù)解析器。

resolveArgument:該方法的返回值就是參數(shù)的具體值,當(dāng)前登錄用戶名從 SecurityContextHolder 中獲取即可(具體參數(shù)松哥的 Spring Security 教程,公號后臺回復(fù) ss)。

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

  1. @Configuration 
  2. public class WebConfig implements WebMvcConfigurer { 
  3.     @Override 
  4.     public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { 
  5.         resolvers.add(new CurrentUserNameHandlerMethodArgumentResolver()); 
  6.     } 

至此,就算配置完成了。

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

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

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

2.PrincipalMethodArgumentResolver

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

  1. @GetMapping("/hello2"
  2. public String hello2(Principal principal) { 
  3.     return "hello " + principal.getName(); 

即直接在當(dāng)前接口的參數(shù)中添加 Principal 類型的參數(shù)即可,該參數(shù)描述了當(dāng)前登錄用戶信息,這個(gè)用過 Spring Security 的小伙伴應(yīng)該都知道(不熟悉 Spring Security 的小伙伴可以在公眾號【江南一點(diǎn)雨】后臺回復(fù) ss)。

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

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

  1. public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.  
  3.  @Override 
  4.  public boolean supportsParameter(MethodParameter parameter) { 
  5.   return Principal.class.isAssignableFrom(parameter.getParameterType()); 
  6.  } 
  7.  
  8.  @Override 
  9.  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  10.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { 
  11.  
  12.   HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 
  13.   if (request == null) { 
  14.    throw new IllegalStateException("Current request is not of type HttpServletRequest: " + webRequest); 
  15.   } 
  16.  
  17.   Principal principal = request.getUserPrincipal(); 
  18.   if (principal != null && !parameter.getParameterType().isInstance(principal)) { 
  19.    throw new IllegalStateException("Current user principal is not of type [" + 
  20.      parameter.getParameterType().getName() + "]: " + principal); 
  21.   } 
  22.  
  23.   return principal; 
  24.  } 
  25.  

supportsParameter:這個(gè)方法主要是判斷參數(shù)類型是不是 Principal,如果參數(shù)類型是 Principal,就支持。

resolveArgument:這個(gè)方法的邏輯很簡單,首先獲取原生的請求,再從請求中獲取 Principal 對象返回即可。

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

3.RequestParamMapMethodArgumentResolver

松哥再給大家舉個(gè)例子:

  1. @RestController 
  2. public class HelloController { 
  3.     @PostMapping("/hello"
  4.     public void hello(@RequestParam MultiValueMap map) throws IOException { 
  5.         //省略... 
  6.     } 

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

  1. public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.  
  3.  @Override 
  4.  public boolean supportsParameter(MethodParameter parameter) { 
  5.   RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); 
  6.   return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && 
  7.     !StringUtils.hasText(requestParam.name())); 
  8.  } 
  9.  
  10.  @Override 
  11.  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  12.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { 
  13.  
  14.   ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); 
  15.  
  16.   if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) { 
  17.    // MultiValueMap 
  18.    Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve(); 
  19.    if (valueType == MultipartFile.class) { 
  20.     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); 
  21.     return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0)); 
  22.    } 
  23.    else if (valueType == Part.class) { 
  24.     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); 
  25.     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { 
  26.      Collection<Part> parts = servletRequest.getParts(); 
  27.      LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size()); 
  28.      for (Part part : parts) { 
  29.       result.add(part.getName(), part); 
  30.      } 
  31.      return result; 
  32.     } 
  33.     return new LinkedMultiValueMap<>(0); 
  34.    } 
  35.    else { 
  36.     Map<String, String[]> parameterMap = webRequest.getParameterMap(); 
  37.     MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size()); 
  38.     parameterMap.forEach((keyvalues) -> { 
  39.      for (String value : values) { 
  40.       result.add(key, value); 
  41.      } 
  42.     }); 
  43.     return result; 
  44.    } 
  45.   } 
  46.  
  47.   else { 
  48.    // Regular Map 
  49.    Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve(); 
  50.    if (valueType == MultipartFile.class) { 
  51.     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); 
  52.     return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0)); 
  53.    } 
  54.    else if (valueType == Part.class) { 
  55.     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); 
  56.     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { 
  57.      Collection<Part> parts = servletRequest.getParts(); 
  58.      LinkedHashMap<String, Part> result = CollectionUtils.newLinkedHashMap(parts.size()); 
  59.      for (Part part : parts) { 
  60.       if (!result.containsKey(part.getName())) { 
  61.        result.put(part.getName(), part); 
  62.       } 
  63.      } 
  64.      return result; 
  65.     } 
  66.     return new LinkedHashMap<>(0); 
  67.    } 
  68.    else { 
  69.     Map<String, String[]> parameterMap = webRequest.getParameterMap(); 
  70.     Map<String, String> result = CollectionUtils.newLinkedHashMap(parameterMap.size()); 
  71.     parameterMap.forEach((keyvalues) -> { 
  72.      if (values.length > 0) { 
  73.       result.put(keyvalues[0]); 
  74.      } 
  75.     }); 
  76.     return result; 
  77.    } 
  78.   } 
  79.  } 
  80.  

supportsParameter:參數(shù)類型是 Map,并且使用了 @RequestParam 注解,并且 @RequestParam 注解中沒有配置 name 屬性,就可以使用該參數(shù)解析器。

resolveArgument:具體解析分為兩種情況:MultiValueMap 和其他 Map,前者中又分三種情況:MultipartFile、Part 或者其他普通請求,前兩者可以處理文件上傳,第三個(gè)就是普通參數(shù)。如果是普通 Map,則直接獲取到原始請求參數(shù)放到一個(gè) Map 集合中返回即可。

4.小結(jié)

前面和大家聊的都是幾種簡單的情況,還有復(fù)雜的如 PathVariableMethodArgumentResolver 和 RequestParamMethodArgumentResolver 松哥以后再和大家詳細(xì)聊。同時(shí)還有一個(gè)問題就是這些參數(shù)解析器具體是在哪里調(diào)用的,這個(gè)也會在松哥近期的 SpringMVC 源碼解析系列文章中和大家分享,好啦,今天周末,就這點(diǎn)簡單的小知識祝大家周末愉快~

原文地址:https://mp.weixin.qq.com/s/4c-uV8f6x5UPvxec6Em79A

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91狠狠| 国内精品九一在线播放 | 午夜看片a福利在线观看 | 青草国产福利视频免费观看 | 免费永久观看美女视频网站网址 | 韩国美女豪爽一级毛片 | 久久青青草原精品国产软件 | 亚洲成人国产精品 | 香蕉人人超人人超碰超国产 | 2020年国产精品午夜福利在线观看 | 日本搜子同屋的日子2国语 日本爽p大片免费观看 | 9色视频在线观看 | 亚州精品永久观看视频 | 成人免费影院 | 97蝌蚪自拍自窝 | 国产偷啪视频一区 | bb18lv黑料正能量 | 四虎永久在线精品波多野结衣 | 免费看www| 精品日韩欧美一区二区三区 | 午夜久久免费视频 | 国产精品久久久久毛片 | 奇米影视在线视频 | 国产精品久久毛片完整版 | 污污在线免费观看 | 婷婷国产在线 | 国产人成激情视频在线观看 | 午夜无码国产理论在线 | 丰满艳妇亲伦视频 | 欧美人与物videos另类3d | 色色色色色色网 | 精品国产麻豆AV无码 | 久久香蕉电影 | 欧美一级片免费看 | 九草视频在线 | 国产成人cao在线 | 特黄视频 | 欧美专区亚洲 | 9l桃色| 亚洲高清在线天堂精品 | 欧美一级二级片 |