日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

mybatis是什么_为什么SpringMVC可以正确解析方法参数名称,但MyBatis却不行?

發(fā)布時(shí)間:2024/9/19 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis是什么_为什么SpringMVC可以正确解析方法参数名称,但MyBatis却不行? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

對(duì)Java字節(jié)碼有一定了解的朋友應(yīng)該知道,Java 在編譯的時(shí)候,默認(rèn)不會(huì)保留方法參數(shù)名,因此我們無(wú)法在運(yùn)行時(shí)獲取參數(shù)名稱。但是在使用 SpringMVC 的時(shí)候,我發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象:當(dāng)我們需要接收請(qǐng)求參數(shù)的時(shí)候,相應(yīng)的 Controller 方法只需要正常聲明,就可以直接接收正確的參數(shù),例如:

注:以下例子使用 maven 進(jìn)行編譯,且非 SpringBoot 項(xiàng)目,SpringBoot 已經(jīng)自動(dòng)解決了參數(shù)名解析的問(wèn)題,后面咱們會(huì)討論@RestController@RequestMapping("calculator")public class CalculatorController { @GetMapping("add") public int add(int aNum, int bNum) { return aNum + bNum; }}

當(dāng)接收到 /calculator/add?aNum=12&bNum=3 這樣的請(qǐng)求時(shí),會(huì)返回 15,即aNum 和 bNum 都能被正確解析。

然而,當(dāng)我們使用 MyBatis 時(shí),如果接口方法有多個(gè)參數(shù)而且我們沒(méi)有打上 @Param 注解的話,執(zhí)行的時(shí)候就會(huì)報(bào)錯(cuò)。例如,我們有如下的接口:

@Mapper

public interface AccountMapper {

Account getByNameAndMobilePhone(String name, String mobilePhone);

}

方法中包含兩個(gè)參數(shù),但是沒(méi)有打上 @Param 注解,這時(shí)候如果調(diào)用這個(gè)方法,會(huì)報(bào)錯(cuò):

org.apache.ibatis.binding.BindingException: Parameter 'name' not found.

Available parameters are [arg1, arg0, param1, param2]

從錯(cuò)誤信息中可以看出,是因?yàn)?MyBatis 沒(méi)有正確解析方法參數(shù)名稱導(dǎo)致異常。

這就很奇怪了,為什么 Spring 可以正確解析方法參數(shù)名稱,但是 MyBatis 卻不行?Java編譯的時(shí)候默認(rèn)會(huì)將方法參數(shù)名抹除,但我并沒(méi)有做特殊處理,Spring 又是從哪里找到方法參數(shù)名的呢?

帶著這些問(wèn)題,我開(kāi)始進(jìn)行研究和探索。

# 獲取參數(shù)名的方式

通過(guò)查閱各種資料,我知道了獲取參數(shù)名稱的方式。

-g 參數(shù)

當(dāng)我們對(duì) Java 源碼進(jìn)行編譯時(shí),無(wú)論是直接使用命令行還是使用 IDE 為我們編譯,實(shí)際上最終都是調(diào)用 javac 命令進(jìn)行的,在編譯的時(shí)候,我們?nèi)绻砑由?-g 參數(shù),即告訴編譯器,我們需要調(diào)試信息,這時(shí),生成的字節(jié)碼當(dāng)中就會(huì)包含局部變量表的信息(方法參數(shù)也是局部變量),于是我們就可以通過(guò)解析字節(jié)碼獲取參數(shù)名了。

我們用最最經(jīng)典的 HelloWorld 程序中的 main 方法為例,看一下編譯的效果:

public class HelloWorld{ public static void main(String[] argsName){ System.out.println("HelloWorld!"); }}

我們直接執(zhí)行如下 javac 命令來(lái)編譯并使用 javap 命令查看生成的字節(jié)碼信息:

javac HelloWorld.javajavap -verbose HelloWorld.class

可以看到,我們的參數(shù)名 argsName 已經(jīng)被抹掉了。而如果字節(jié)碼中都沒(méi)有我們所需要的信息,那么在運(yùn)行時(shí),反射或者是別的方法也都無(wú)能為力了,巧婦難為無(wú)米之炊吶。

接下來(lái),我們?cè)囈幌绿砑?-g 參數(shù)會(huì)發(fā)生什么:

javac -g HelloWorld.javajavap -verbose HelloWorld.class

可以看到,這里多了一個(gè) LocalVariableTable,即局部變量表,其中就有我們的參數(shù)名稱 argsName!

那么,我們?nèi)绾卧诜椒ㄟ\(yùn)行時(shí)從字節(jié)碼信息中獲取參數(shù)名稱呢?你可以直接通過(guò) javap 來(lái)獲取字節(jié)碼信息,然后自己去根據(jù)信息的格式去解析,然而這樣太低效了,而且太繁瑣了。

# ASM 框架

這時(shí)候如果我們請(qǐng)大名鼎鼎的 ASM 來(lái)當(dāng)“導(dǎo)游”,帶著我們游覽字節(jié)碼內(nèi)部構(gòu)造,實(shí)現(xiàn)起來(lái)就輕松多了。

這個(gè) ASM 可牛了,它不僅可以查看字節(jié)碼的信息,甚至可以動(dòng)態(tài)修改類的定義或者新建一個(gè)原本沒(méi)有的類!在各種框架中被廣泛地使用,SpringAOP中使用的 CGLib 底層就是使用 ASM 來(lái)實(shí)現(xiàn)的。有興趣可以查看官網(wǎng):https://asm.ow2.io/ 之前我也寫過(guò)一篇文章《Java用ASM寫一個(gè)HelloWorld程序》,有興趣可以看一下。

言歸正傳,如何通過(guò) ASM 來(lái)獲取參數(shù)名稱呢? 直接上代碼:

asm asm 3.3.1/*** 使用字節(jié)碼工具ASM來(lái)獲取方法的參數(shù)名*/public static String[] getMethodParamNames(final Method method) throws IOException { final int methodParameterCount = method.getParameterTypes().length; final String[] methodParametersNames = new String[methodParameterCount]; ClassReader cr = new ClassReader(method.getDeclaringClass().getName()); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cr.accept(new ClassAdapter(cw) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); final Type[] argTypes = Type.getArgumentTypes(desc); //參數(shù)類型不一致 if (!method.getName().equals(name) || !matchTypes(argTypes, method.getParameterTypes())) { return mv; } return new MethodAdapter(mv) { @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { //如果是靜態(tài)方法,第一個(gè)參數(shù)就是方法參數(shù),非靜態(tài)方法,則第一個(gè)參數(shù)是 this, 然后才是方法的參數(shù) int methodParameterIndex = Modifier.isStatic(method.getModifiers()) ? index : index - 1; if (0 <= methodParameterIndex && methodParameterIndex < methodParameterCount) { methodParametersNames[methodParameterIndex] = name; } super.visitLocalVariable(name, desc, signature, start, end, index); } }; } }, 0); return methodParametersNames; } /** * 比較參數(shù)是否一致 */private static boolean matchTypes(Type[] types, Class>[] parameterTypes) { if (types.length != parameterTypes.length) { return false; } for (int i = 0; i < types.length; i++) { if (!Type.getType(parameterTypes[i]).equals(types[i])) { return false; } } return true;}

簡(jiǎn)而言之,ASM使用了訪問(wèn)者模式,它就像一個(gè)導(dǎo)游,帶著我們?nèi)ビ斡[字節(jié)碼文件中的各個(gè)“景點(diǎn)”。我們實(shí)現(xiàn)不同的 Visitor 接口就像是手上握有不同景點(diǎn)門票,導(dǎo)游會(huì)帶著 ClassVisitor 去總體參觀類定義的景觀,而類內(nèi)部有方法,如果你想看一下方法內(nèi)部的定義,需要"額外購(gòu)票",即需要實(shí)現(xiàn) MethodVisitor 才能跟著導(dǎo)游去參觀方法定義這個(gè)景點(diǎn)。而在游覽各個(gè)景點(diǎn)的時(shí)候,我們可以只游覽我們感興趣的部分,這就可以繼承適配器(ClassAdapter和MethodAdapter分別是ClassVisitor和MethodVisitor的適配器)然后只實(shí)現(xiàn)我們感興趣的方法即可。

這里對(duì)于類的定義,我們只對(duì)方法感興趣,因此只實(shí)現(xiàn) visitMethod 方法;在方法中,我們只對(duì) LocalVariableTable 有興趣,因此只實(shí)現(xiàn) visitLocalVariable 方法。這樣我們得到了局部變量表,再根據(jù)一些規(guī)則就可以拿到我們的參數(shù)名稱了!是不是很棒!

順便說(shuō)一下,如果你使用 maven 來(lái)管理項(xiàng)目的話,這個(gè) -g 參數(shù)會(huì)在編譯的時(shí)候自動(dòng)加上,因此我們不需要額外添加就可以通過(guò)字節(jié)碼拿到,這也就是為什么 SpringMVC 可以拿到方法參數(shù)名稱的原因。

但是這種方式對(duì)于接口和抽象方法是不管用的,因?yàn)槌橄蠓椒](méi)有方法體,也就沒(méi)有局部變量,自然也就沒(méi)有局部變量表了:

MyBatis 是通過(guò)接口跟 SQL 語(yǔ)句綁定然后生成代理類來(lái)實(shí)現(xiàn)的,因此它無(wú)法通過(guò)解析字節(jié)碼來(lái)獲取方法參數(shù)名。

end:如果你覺(jué)得本文對(duì)你有幫助的話,記得關(guān)注點(diǎn)贊轉(zhuǎn)發(fā),你的支持就是我更新動(dòng)力。

總結(jié)

以上是生活随笔為你收集整理的mybatis是什么_为什么SpringMVC可以正确解析方法参数名称,但MyBatis却不行?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。