为什么SpringMVC能正确解析方法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方入口

对Java字节码有必定了解的朋友应该知道,Java 在编译的时分,默许不会我国高速公路网保存办法参数名,因而咱们无法在运行时获取参数称号。可是在运用 SpringMVC 的时分,我发现一个古怪的现象:当咱们需求接纳恳求参数的时分,相应的 Controller 办法只需求正常声明,就能够直接接纳正确的参数,例如:

注:以下比如运用 maven 进行编译,且非 SpringBoot 项目,SpringBoot 现已主动处理了参数名解析的问题,后边咱们会评论

@Rest上海普天智绿新能源技能有限公司Controller
@RequestMapping("calculator")
public class CalculatorController {
@GetMapping("add")
public int add(int aNum, int bNum) {
return aNum + bNum;
}
}

当接纳到95117怎样转人工 /calculator/ad真渊京马d?aNum=12&bNum=3 这样的恳求时,会回来 15,即aNum 和 bNum 都能被正确解析。


可是,当咱们运用 MyBatis 时,假如接口办法有多个参数并且咱们没有打上 @Param 注解夫妻交流小说的话,履行的时分就会报错。例如,咱们有如下的接口:

@Mapper

public interface AccountMapper {

Account getByNameAndMobilePhone(String name, String mobilePhone);

}

办法中包括两个参数,可是没有打上 @Param 注解,这时分假如调用这个办法,会报错:

org.apache.ibatis.binding.BindingExc刘延宁eption: Parameter 'name' not found.

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

从错误信息中能够看出,是由于 MyBatis 没有正确解析办法参数称号导致反常。

这就很古怪了,为什么 Spring 能够正确解析方为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口法参数称号,可是 MyBatis 却不可?Java编译的时分默许会将办法参数名抹除,但我并没有做特别处理,Spring 又是从哪里找到办法参数名的呢?


带着这些问题,我开端进行研究和探究。


# 获取参数名的办法


经过查阅各种材料,我知道了获取参数称号的办法。

-g 参数

当咱们对 Java 源码进行编译时,无论是直接施华洛世奇项圈运用指令行仍是运用 IDE 为咱们编译,实际上易晓曦终究都是调用 javac 指令进行的,在编译的时分,咱们假如增加上 -g 参数,即告知编译器,咱们需求调试信息,这时,生成的字节码傍边就会包括局部变量表的信息(办法参数也是局部变量),所以咱们就能够经过解析字节码获取参数名了。

我为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口们用最最经典的 HelloWorld 程序中的 main 办法为例,看一下编译的作用:


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

咱们直接履行如下 javac 指令来编译并运用 javap 指令检查生成的字节码信息:


javac HelloWorld.java
javap -verbose HelloWorld.class

能够看到,咱们的参数名 argsName 现已被抹掉了。而假如字节码中都没有咱们所需求的信息,那么在运行时,反射或许是其他办法也都力不从心了,巧妇难为无米之炊呐。

接下来,咱们试一下增加 -g 参数会发作什么:


javac -g HelloWorld.java
javap -verbose He为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口lloWorld.class

能够看到,这儿多了一个 LocalVariableTable,即局部变量表,其间就有咱们的参数称号 argsName!


那么,咱们如安在办法运行时从字节码信息中获取参数称号呢?你能够直接经过 javap 来获取字节码信息,然后自己去依据信息的格局去解析,可是这样太低效了,并且太繁琐了。

# ASM 结构

这为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口时分假如咱们请大名鼎鼎的 ASM 来当“导游”,带着咱们旅游字节码内部结构,完成起来就轻松多了。

这个 ASM 可牛了,它不仅能够检查字节码的信息,乃至能够动态修正类的界说或许为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口新建一个本来没有的类!在各种结构中被广泛地运用,SpringAOP中运用的 CGLib 底层便是运用 ASM 来完成的。有爱好能够检查官网:https://asm.ow2.io/ 之前我也写过一篇文章《Java用ASM写一个HelloWorld程序》,有爱好能够看一下。

言归正传,怎么经过 ASM 来获取参数称号呢?delete 直接上代码:



asm
asm
3.3.1

/**
* 运用字节码东西ASM来获取办法的参数名
*/
publ金樱子ic static String[] getMethodParamNames(final Method method) throws IOException {
final int methodParameterCount = method.getParameterTypes().length;
final Str顾彦深ing[] methodParametersNames = new String[methodParameterCount];
ClassReader cr = new ClassReader(method.g流etDeclqwqshowaringClass().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);

//参数类型不共同
if (!method.getName().equals(name) || !matchTypes(argTypes, method.getParameterTypes())) {
return mv;
}

return new Meth21世纪教育网odAdapter(mv) {
@Override
public void visitLoca八角lVariable(String name, String desc, String signature, Label start, Label end, int index) {

//假如是静态为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口办法,第一个参数便是办法参数,非静态办法,则第一个参数是 this, 然后才是办法的参数
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;
}

/**
* 比较参数是否共同
*/
private static boolean matchTypes(Type[] types, Class
if (types.length != parameterType为什么SpringMVC能正确解析办法参数名,而MyBatis不能?-必威app_betway必威体育app官网|官方进口s.length) {
return false;
}
for (int i = 0; i < types.length; i++) {
if (!Type.getType(parameterTypes[i]).equals(types[i])) {
return false;
}
}
return true;
}

简而言之,ASM运用了访问者形式,它就像一个导游,带着咱们去旅游字节码文件中的各个“景点”。咱们完成不同的 Visitor 接口就像是手上握有不同景点门票,导游会带着 ClassVisitor 去整体观赏类界说的景象4008333000,而类内部有办法,假如你想看一下办法内部的界说,需求"额定购票",即需求完成 MethodVisitor 才干跟着导游去观赏办法界说这个景点。而在旅游各个景点的时分,咱们能够只旅游咱们感爱好的部分,这就能够承继适配器(ClassAdapter和MethodAdapter分别是ClassVisitor和MethodVisitor的适配器)然后只完成咱们感爱好的办法即可。

这儿关于类的界说,咱们只对办法感爱好,因而只完成 visitMethod 办法;在办法中,咱们只对 LocalVariableTable 有爱好,因而只完成 visit开国大典LocalVariable 办法。这样咱们得到了局部变量表,再依据一些规矩就能够拿到咱们的参数称号了!是不是很棒!


趁便说一下,假如你运用 maven 来办理项目的话,这个 -g 参数会在编译的时分主动加上,因而咱们不需求额定增加就能够经过字节码拿到,这也便是为什么 SpringMVC 能够拿到办法参数称号的原因。

可是这种办法关于接口和笼统办法是不管用的,由于笼统办法没有办法体,也就没有局部变量,天然也就没有局部变量表了:

MyBatis 是经过接口跟 SQL 华为路由器句子绑定然后生成署理类来完成的,因而它无法经过解析字节码来获取办法参数名。

end:假如你觉得本文对你有协助的话,记住关注点赞转发,你的支撑便是我更新动力。

评论(0)