Spring5裡邊的新玩法!這種URL請求讓我漲見識了!

Spring5也已經出來好久了,裡邊有一些新玩法也需要我們去慢慢揭開面紗,這不,松哥最近在研究SpringMVC源碼的時候,就看到這樣一段程式碼:

protected String initLookupPath(HttpServletRequest request){

if(usesPathPatterns()){

request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);

RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);

String lookupPath =requestPath.pathWithinApplication().value();

returnUrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);

}

else {

return getUrlPathHelper().resolveAndCacheLookupPath(request);

}

}

這個方法就是Spring5裡邊出來的,以前是沒有這個方法的。在舊的SpringMVC中,當我們需要獲取當前請求地址的時候,直接通過如下管道獲取:

String lookupPath =this.getUrlPathHelper().getLookupPathForRequest(request);

但是現在變了,現在獲取當前請求URL地址時,管道如下:

String lookupPath = initLookupPath(request);

兩種方式相比,主要是initLookupPath方法中多了usesPathPatterns選項,這是Spring5中的新玩意,所以今天松哥就通過一篇簡單的文章來和大家分享一下usesPathPatterns到底是什麼,該怎麼玩!

這可不是一個小變化哦!特別是如果你在項目中使用了WebFlux,那麼這個東西就顯得尤為重要了!

AntPathMatcher

當我們使用@RequestMapping注解去標記請求介面的時候(或者使用它的類似方法如@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping),我們可以使用一些萬用字元去匹配URL地址,舉個簡單例子,假設我有下麵五個介面:

@GetMapping(“/hello/**/hello”)

public String hello(){

return“/hello/**/hello”;

}

@GetMapping(“/h?llo”)

public String hello2(){

return“/h?llo”;

}

@GetMapping(“/**/*.html”)

public String hello3(){

return“/**/*.html”;

}

@GetMapping(“/hello/{p1}/{p2}”)

public String hello4(@PathVariable String p1,@PathVariable String p2){

System.out.println(“p1 =”+ p1);

System.out.println(“p2 =”+ p2);

return“/hello/{p1}/{p2}”;

}

@GetMapping(“/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}”)

public void handle(@PathVariable String name,@PathVariable String version,@PathVariable String ext){

System.out.println(“name =”+ name);

System.out.println(“version =”+ version);

System.out.println(“ext =”+ ext);

}

在解釋介面的含義之前,先來說說這幾個萬用字元的含義:

萬用字元含義

**匹配0個或者多個目錄

*匹配0個或者多個字元

?匹配任意單個字元

瞭解了萬用字元的含義,我們再來說說各個介面都能接收哪些請求:

第一個介面,可以接收諸如/hello/123/123/hello、/hello/a/hello以及/hello/hello這樣的請求,因為中間的**代表0個或者多個目錄。

第二個介面,可以接收諸如/hallo、/hello、/hMllo之類的請求,注意它不能接收/haallo或者/hllo,因為?表示一個字元。

第三個介面可以接收任意以.html為尾碼的請求,例如/aaa/bb/cc.html、/aa.html或者/aa/aa.html。

第四個介面估計大家都比較熟悉,在RESTful風格的介面設計中估計大家都用過,它接收的請求格式類似於/hello/aa/bb,其中參數p1就對應aa,參數p2對應bb。

第五個介面則用到了正則,name、version以及ext三個參數格式用正則表達出來,它可以接收諸如/spring-web-3.0.5.jar格式的請求,最終的參數name就是spring-web,version就是3.0.5,ext則是.jar。

這是SpringMVC中之前就存在的功能,不管你用沒用過,反正它一致存在。

那麼是誰支撐了這個功能呢?那就是AntPathMatcher。

AntPathMatcher是一個實現了Ant風格的路徑匹配器,Ant風格的路徑規則實際上就是我們前面給大家介紹的那三種路徑匹配符,很Easy。這種路徑匹配規則源自Apache Ant項目(https://ant.apache.org),Apache Ant我們現在其實已經很少會用到了,它的替代品就是大家所熟知的Maven,如果你有幸維護一些2010年之前的老項目的話,有可能會接觸到Ant。

AntPathMatcher實際上在SpringMVC中有非常廣泛的應用,不僅僅是在@RequestMapping中定義介面用到,在其他一些涉及到地址匹配的地方也會用到,例如我們在SpringMVC的設定檔中配寘靜態資源過濾時,也是Ant風格路徑匹配:

<mvc:resources mapping=“/**”location=“/”/>

另外像攔截器裏的攔截路徑注册、跨域處理時的路徑匹配等等,都會用到Ant風格的路徑匹配符。

整體上來說,AntPathMatcher是Spring中一種比較原始的路徑匹配解決方案,雖然比較簡單,但是它的效率很低,並且在處理URL編碼的時候也很不方便。

囙此,才有了Spring5中的PathPattern。

PathPattern

PathPattern專為Web應用設計,它與之前的AntPathMatcher功能大部分比較類似,當然也有一些細微差异,這個松哥後面會說。

如果是Servlet應用,現時官方推薦的URL匹配解決方案就是PathPattern(當然你也可以選擇較早的AntPathMatcher),雖然官方推薦的是PathPattern,但實際上默認使用的依然是AntPathMatcher;如果你用的是WebFlux,PathPattern就是唯一解決方案了。

注意,PathPattern是一個非常新鮮的玩藝,現時Spring最新版是5.3.4,在Spring5.3之前,我們在Servlet應用中,也只能選擇AntPathMatcher,從Spring5.3之後,我們才可以使用PathPattern了。

PathPattern會將URL規則預解析為PathContainer,它對URL地址匹配的處理更加快速,PathPattern與AntPathMatcher的差异主要體現在兩個方面:

第一,PathPattern只支持結尾部分使用**,如果在路徑的中間使用**就會報錯,上文中第一個和第三個介面,在PathPattern模式下會報錯,如下:

因為在中間或者開始使用**極易造成混亂,囙此PathPattern只支持在結尾使用**。

第二,PathPattern支持使用諸如{*path}的管道進行路徑匹配,這種寫法也可以匹配到多層路徑,並且將匹配到的值賦值給path變數,例如如下一個介面:

@GetMapping(“/javaboy/{*path}”)

public void hello6(@PathVariable String path){

System.out.println(“path =”+ path);

}

如果請求路徑是http://localhost:8080/javaboy/aa,那麼參數path的值就是/aa;

如果請求路徑是http://localhost:8080/javaboy/aa/bb/cc/dd,那麼參數path的值就是/aa/bb/cc/dd;

這個寫法也比較新穎,因為之前的AntPathMatcher裡邊沒有這個。

如何使用

默認情况下,SpringMVC中使用的還是AntPathMatcher,那麼如何開啟PathPattern呢?很簡單,在SpringBoot項目中只需要添加如下配寘即可:

@Configuration

public class WebConfig implements WebMvcConfigurer {

@Override

public void configurePathMatch(PathMatchConfigurer configurer){

configurer.setPatternParser(new PathPatternParser());

}

}

添加了這個配寘後,在我們文章一開始貼出來的程式碼裏,就會進入到if分支中,進而使用PathPattern去解析請求URL。

文章來源於:http://www.xmfindbbs.com