国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring mvc中的@PathVariable動態參數詳解

spring mvc中的@PathVariable動態參數詳解

2022-03-08 00:35cc_yy_zh Java教程

這篇文章主要介紹了spring mvc中的@PathVariable動態參數詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

spring mvc @PathVariable動態參數

spring mvc中的@PathVariable是用來獲得請求url中的動態參數的,十分方便。

@Controller  
public class TestController {  
   @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
   public String getLogin(@PathVariable("userId") String userId,  
       @PathVariable("roleId") String roleId){  
       System.out.println("User Id : " + userId);  
       System.out.println("Role Id : " + roleId);  
       return "hello";  
   }  
   @RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
   public String getProduct(@PathVariable("productId") String productId){  
         System.out.println("Product Id : " + productId);  
         return "hello";  
   }  
   @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}",  
         method = RequestMethod.GET)  
   public String getRegExp(@PathVariable("regexp1") String regexp1){  
         System.out.println("URI Part 1 : " + regexp1);  
         return "hello";  
   }  
}  

 

spring mvc是如何做到根據參數名動態綁定參數的?

使用過SpringMVC的同學都知道,當我們需要在Controller層接收客戶端的請求參數時,只需要在形參上加@RequestParam注解,SpringMVC就會自動幫我們做參數綁定,如下示例:

@GetMapping("test1")
public void test1(@RequestParam("name") String name, @RequestParam("age") Integer age) {
}

客戶端請求示例:

curl http://127.0.0.1:8080/test1?name=root&age=18

每個參數都加注解寫起來非常的麻煩,因此SpringMVC還可以根據參數名自動匹配,只要方法的參數名和客戶端請求的參數名相同即可綁定,代碼可以簡化為:

@GetMapping("test2")
public void test2(String name, Integer age) throws Exception {

}

SpringMVC是如何做到的呢???

反射獲取參數名

熟悉SpringMVC的同學都知道,SpringMVC通過一個DispatcherServlet來分發客戶端的請求,根據請求的URI映射對應的處理器Handler,將請求交給對應的Handler處理,說白了就是通過反射的方式調用Controller的方法,然后將請求的參數解析,并和方法的形參做匹配并傳遞過去。

要想綁定參數,首先要做的就是知曉Controller的方法需要的參數名是什么???

對于第一種寫法,很好理解,方法想要的參數名就是@RequestParam注解的值,只需要通過反射來獲取即可,如下代碼:

public static void main(String[] args) throws Exception {
	Method test1 = UserController.class.getMethod("test1", String.class, Integer.class);
	for (Parameter parameter : test1.getParameters()) {
		RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
		System.err.println("test1-參數名:" + requestParam.value());
	}
}
控制臺輸出:
test1-參數名:name
test1-參數名:age

但是對于第二種簡化的寫法,是無法通過反射來獲取參數名稱的,如下:

public static void main(String[] args) throws Exception {
	Method test2 = UserController.class.getMethod("test2", String.class, Integer.class);
	for (Parameter parameter : test2.getParameters()) {
		System.err.println("test2-參數名:"+parameter.getName());
	}
}

你們猜猜拿到的參數名是什么???

spring mvc中的@PathVariable動態參數詳解

竟然是沒有任何意義的arg0、arg1!!!

這是為什么呢???

熟悉JVM的同學都知道,Java代碼要想在JVM里執行,首先需要通過javac命令編譯成字節碼Class文件,而這個編譯的過程會直接將方法的參數名稱丟棄,變成無意義的arg0、arg1…,因此通過反射是無法獲取參數名稱的。

-parameters參數

既然反射獲取不到參數名是因為編譯時丟棄了,那么有沒有辦法讓javac編譯時將參數名保留下來呢???答案是有的,那就是-parameters參數。

JDK8加入了一個新功能,編譯時加上-parameters參數,即可保留參數名,通過parameter.getName()就可以獲取到正常的參數名了。

示例

有如下測試類:

public class Demo {
public void test(String name, Integer age) {
}
}
javac Demo.java #默認的編譯方式
javap -verbose Demo

spring mvc中的@PathVariable動態參數詳解

javac -parameters Demo.java #加-parameters參數編譯
javap -verbose Demo

spring mvc中的@PathVariable動態參數詳解

可以看到,加了-parameters參數后,字節碼文件會使用額外的MethodParameters區域來保存方法的參數名稱。這樣反射的時候通過parameter.getName()就可以獲取到參數名了。

注意:只支持JDK8及以上版本!!!

-g參數

由于-parameters要求JDK至少是8版本,而SpringMVC肯定是要支持低版本JDK的,那么還有沒有其他方法可以保留參數名呢???

答案依然是有的,那就是-g參數。

編譯時,加上-g參數就是告訴編譯器,我們需要調試類的信息,這時編譯器在編譯時,就會保留局部變量表的信息,參數也是局部變量表的一部分。

spring mvc中的@PathVariable動態參數詳解

可以看到,加上-g后就可以從局部變量表中獲取參數的名稱了。

使用Maven來管理項目的話,編譯會默認加-g參數,不需要開發者介入。

注意:雖然-g會將局部變量表的信息保存下來,但是依然無法通過反射parameter.getName()的方式來獲取參數名,需要開發者去解析Class字節碼文件來獲取,這是和-parameters的一個重大區別!!!

ASM

ASM是一個通用的Java字節碼操作和分析框架。 它可以用于修改現有類或直接以二進制形式動態生成類。 ASM提供了一些常見的字節碼轉換和分析算法,可以從中構建自定義復雜轉換和代碼分析工具。 ASM提供與其他Java字節碼框架類似的功能,但專注于性能。 因為它的設計和實現盡可能小而且快,所以它非常適合在動態系統中使用(但當然也可以以靜態方式使用,例如在編譯器中)。

編譯時加上-g參數可以將參數名保留下來,但是依然無法通過反射來獲取,需要解析字節碼文件自己獲取。

有沒有好用的工具包來幫我們解析字節碼文件呢???

答案依然是:有的。

Java通過ASM就可以很方便的操作字節碼文件,很多開源框架都用到了ASM,例如CGLIB。

下面寫一個例子,通過ASM來獲取方法的參數名。

1、引入依賴

<dependency>
  <groupId>asm</groupId>
  <artifactId>asm-util</artifactId>
  <version>3.3.1</version>
</dependency>

2、代碼示例

public class Demo {
	public void test(String name, Integer age) {
	}
	/**
	 * 通過ASM來訪問參數名
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		Class<Demo> clazz = Demo.class;
		Method method = clazz.getMethod("test", String.class, Integer.class);
		InputStream in = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
		ClassReader cr = new ClassReader(in);
		ClassNode cn = new ClassNode();
		cr.accept(cn, ClassReader.EXPAND_FRAMES);
		List<MethodNode> methodNodes = cn.methods;
		for (MethodNode methodNode : methodNodes) {
			if (method.getName().equals(methodNode.name)) {
				System.err.println("test方法參數:");
				List<LocalVariableNode> localVariables = methodNode.localVariables;
				for (LocalVariableNode localVariable : localVariables) {
					System.err.println(localVariable.name);
				}
			}
		}
	}
}

控制臺輸出:

test方法參數:

this

name

age

注意:這種方式對接口和抽象方法沒有用,因為抽象方法沒有方法體,也就沒有局部變量表。這也就是為什么MyBatis在xml中無法根據接口方法的參數名去綁定參數的原因!!!

至此,我們已經知道,Java獲取方法的參數名有兩種方式,分別是加-parameters參數反射獲取、-g參數通過ASM解析字節碼文件獲取。

那SpringMVC用的是哪種呢???

SpringMVC的處理方式

SpringMVC是如何解決參數名稱的問題的呢?是通過-parameters參數嗎???

當然不是,首先-parameters參數是JDK8才提供的,老版本的JDK根本沒這個功能,SpringMVC是要支持JDK8之前的版本的,而且這種解決方案強制要求開發者編譯時手動加參數,也很不友好。

要想知道SpringMVC的解決方案,必須看源碼!!!

Debug跟蹤源碼的過程筆者就不詳敘了,感興趣的同學可以自己去跟蹤一下。

SpringMVC將一個方法處理器封裝為一個HandlerMethod類,方法的參數則用MethodParameter表示:

spring mvc中的@PathVariable動態參數詳解

MethodParameter有一個獲取參數名的方法getParameterName():

spring mvc中的@PathVariable動態參數詳解

獲取參數名的的任務其實是交給ParameterNameDiscoverer去完成了,這是一個接口,主要的作用就是解析方法的參數名稱。

MethodParameter的ParameterNameDiscoverer實現類是PrioritizedParameterNameDiscoverer。

spring mvc中的@PathVariable動態參數詳解

距離真相只剩一步之遙了,去看看LocalVariableTableParameterNameDiscoverer實現吧。

spring mvc中的@PathVariable動態參數詳解

只要看inspectClass()方法就知道真相了。

spring mvc中的@PathVariable動態參數詳解

可以看到,LocalVariableTableParameterNameDiscoverer底層就是用的ASM的技術來獲取方法的參數名的。只是Spring并沒有直接依賴ASM,而是將他們封裝到了自己的org.springframework.asm包下。

總結

SpringMVC獲取Controller方法的參數名有三種方式,如下:

方案 限制 優缺點
參數加注解 不受限 編寫麻煩
-parameters JDK8及以上才支持 直接通過parameter.getName()獲取,方便
-g 不受限,編譯加-g參數即可 解析比較麻煩,依賴于ASM
  • 如果加了@RequestParam則優先使用注解解析。
  • 如果沒有注解,則采用StandardReflectionParameterNameDiscoverer解析,通過Parameter.getName()反射獲取,前提是JDK版本為8以上,且開啟了-parameters編譯參數。
  • 如果前面2種都無法獲取,則采用LocalVariableTableParameterNameDiscoverer通過ASM技術來解析。

注意:如果編譯不加-g參數,即使是用ASM也無法解析。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/cc_yy_zh/article/details/78953548

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲久久 | 久青草视频 | 成人小视频在线观看 | 久久久久国产精品午夜一区 | www.日韩在线观看 | 国产免费网址 | 成人午夜精品久久久久久久网站 | 狠狠av | 午夜激情视频在线观看 | 成人小视频在线观看 | 日本黄色大片 | 91av原创| 男女羞羞网站 | 精品无码久久久久久国产 | 国产精品久久久久无码av | 91影院在线观看 | 婷婷午夜天 | 91在线看黄| 久久777| 国产一区二区三区免费 | 精品午夜久久 | 九九热精品视频在线免费观看 | 中国大陆高清aⅴ毛片 | 午夜免费电影 | 婷婷五月情| 狠狠天天 | 久久久成人精品 | 久久久精品在线 | √天堂8在线网 | 国产精品第一区 | 欧美视频一区二区 | 国内成人自拍视频 | 网站黄色在线免费观看 | 亚洲精品成人免费 | 在线日韩成人 | 久久国产精品久久 | 国产在线精品一区二区 | 亚洲精品一二三 | 日韩在线免费 | 欧美成人精品一区二区男人看 | 精品国偷自产在线 |