导游网站源码分享?导游平台app

本篇文章给大家谈谈导游网站源码分享,以及导游平台app对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。

对Java字节码有一定了解的朋友应该知道,Java在编译的时候,默认不会保留方法参数名,因此我们无法在运行时获取参数名称。但是在使用SpringMVC的时候,我发现一个奇怪的现象:当我们需要接收请求参数的时候,相应的Controller方法只需要正常声明,就可以直接接收正确的参数

[Java]纯文本查看复制代码

@RestController

@RequestMapping(“calculator”)

publicclassCalculatorController{

@GetMapping(“add”)

publicintadd(intaNum,intbNum){

returnaNum+bNum;

}

}

当接收到/calculator/add?aNum=12&bNum=3这样的请求时,会返回15,即aNum和bNum都能被正确解析。

然而,当我们使用MyBatis时,如果接口方法有多个参数而且我们没有打上@Param注解的话,执行的时候就会报错。例如,我们有如下的接口:

[Java]纯文本查看复制代码

@Mapper

publicinterfaceAccountMapper{

AccountgetByNameAndMobilePhone(Stringname,StringmobilePhone);

}

方法中包含两个参数,但是没有打上@Param注解,这时候如果调用这个方法,会报错:

org.apache.ibatis.binding.BindingException:Parameter’name’notfound.

Availableparametersare[arg1,arg0,param1,param2]

从错误信息中可以看出,是因为MyBatis没有正确解析方法参数名称导致异常。

这就很奇怪了,为什么Spring可以正确解析方法参数名称,但是MyBatis却不行?Java编译的时候默认会将方法参数名抹除,但我并没有做特殊处理,Spring又是从哪里找到方法参数名的呢?

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

ASM框架

这时候如果我们请大名鼎鼎的ASM来当“导游”,带着我们游览字节码内部构造,实现起来就轻松多了。

这个ASM可牛了,它不仅可以查看字节码的信息,甚至可以动态修改类的定义或者新建一个原本没有的类!在各种框架中被广泛地使用,SpringAOP中使用的CGLib底层就是使用ASM来实现的。有兴趣可以查看官网:https://asm.ow2.io/之前我也写过一篇文章《Java用ASM写一个HelloWorld程序》,有兴趣可以看一下。

言归正传,如何通过ASM来获取参数名称呢?直接上代码:

[XML]纯文本查看复制代码

<dependency>

<groupId>asm</groupId>

<artifactId>asm</artifactId>

<version>3.3.1</version>

</dependency>

[Java]纯文本查看复制代码

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

/**

*使用字节码工具ASM来获取方法的参数名

*/

publicstaticString[]getMethodParamNames(finalMethodmethod)throwsIOException{

finalintmethodParameterCount=method.getParameterTypes().length;

finalString[]methodParametersNames=newString[methodParameterCount];

ClassReadercr=newClassReader(method.getDeclaringClass().getName());

ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);

cr.accept(newClassAdapter(cw){

@Override

publicMethodVisitorvisitMethod(intaccess,Stringname,Stringdesc,Stringsignature,String[]exceptions){

MethodVisitormv=super.visitMethod(access,name,desc,signature,exceptions);

finalType[]argTypes=Type.getArgumentTypes(desc);

//参数类型不一致

if(!method.getName().equals(name)||!matchTypes(argTypes,method.getParameterTypes())){

returnmv;

}

returnnewMethodAdapter(mv){

@Override

publicvoidvisitLocalVariable(Stringname,Stringdesc,Stringsignature,Labelstart,Labelend,intindex){

//如果是静态方法,第一个参数就是方法参数,非静态方法,则第一个参数是this,然后才是方法的参数

intmethodParameterIndex=Modifier.isStatic(method.getModifiers())?index:index-1;

if(0<=methodParameterIndex&&methodParameterIndex<methodParameterCount){

methodParametersNames[methodParameterIndex]=name;

}

super.visitLocalVariable(name,desc,signature,start,end,index);

}

};

}

},0);

returnmethodParametersNames;

}

/**

*比较参数是否一致

*/

privatestaticbooleanmatchTypes(Type[]types,Class<?>[]parameterTypes){

if(types.length!=parameterTypes.length){

returnfalse;

}

for(inti=0;i<types.length;i++){

if(!Type.getType(parameterTypes[i]).equals(types[i])){

returnfalse;

}

}

returntrue;

}

简而言之,ASM使用了访问者模式,它就像一个导游,带着我们去游览字节码文件中的各个“景点”。我们实现不同的Visitor接口就像是手上握有不同景点门票,导游会带着ClassVisitor去总体参观类定义的景观,而类内部有方法,如果你想看一下方法内部的定义,需要”额外购票”,即需要实现MethodVisitor才能跟着导游去参观方法定义这个景点。而在游览各个景点的时候,我们可以只游览我们感兴趣的部分,这就可以继承适配器(ClassAdapter和MethodAdapter分别是ClassVisitor和MethodVisitor的适配器)然后只实现我们感兴趣的方法即可。

这里对于类的定义,我们只对方法感兴趣,因此只实现visitMethod方法;在方法中,我们只对LocalVariableTable有兴趣,因此只实现visitLocalVariable方法。这样我们得到了局部变量表,再根据一些规则就可以拿到我们的参数名称了!是不是很棒!

顺便说一下,如果你使用maven来管理项目的话,这个-g参数会在编译的时候自动加上,因此我们不需要额外添加就可以通过字节码拿到,这也就是为什么SpringMVC可以拿到方法参数名称的原因。

但是这种方式对于接口和抽象方法是不管用的,因为抽象方法没有方法体,也就没有局部变量,自然也就没有局部变量表了:

MyBatis是通过接口跟SQL语句绑定然后生成代理类来实现的,因此它无法通过解析字节码来获取方法参数名。对Java字节码有一定了解的朋友应该知道,Java在编译的时候,默认不会保留方法参数名,因此我们无法在运行时获取参数名称。但是在使用SpringMVC的时候,我发现一个奇怪的现象:当我们需要接收请求参数的时候,相应的Controller方法只需要正常声明,就可以直接接收正确的参数

[Java]纯文本查看复制代码

@RestController

@RequestMapping(“calculator”)

publicclassCalculatorController{

@GetMapping(“add”)

publicintadd(intaNum,intbNum){

returnaNum+bNum;

}

}

当接收到/calculator/add?aNum=12&bNum=3这样的请求时,会返回15,即aNum和bNum都能被正确解析。

然而,当我们使用MyBatis时,如果接口方法有多个参数而且我们没有打上@Param注解的话,执行的时候就会报错。例如,我们有如下的接口:

[Java]纯文本查看复制代码

@Mapper

publicinterfaceAccountMapper{

AccountgetByNameAndMobilePhone(Stringname,StringmobilePhone);

}

方法中包含两个参数,但是没有打上@Param注解,这时候如果调用这个方法,会报错:

org.apache.ibatis.binding.BindingException:Parameter’name’notfound.

Availableparametersare[arg1,arg0,param1,param2]

从错误信息中可以看出,是因为MyBatis没有正确解析方法参数名称导致异常。

这就很奇怪了,为什么Spring可以正确解析方法参数名称,但是MyBatis却不行?Java编译的时候默认会将方法参数名抹除,但我并没有做特殊处理,Spring又是从哪里找到方法参数名的呢?

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

ASM框架

这时候如果我们请大名鼎鼎的ASM来当“导游”,带着我们游览字节码内部构造,实现起来就轻松多了。

这个ASM可牛了,它不仅可以查看字节码的信息,甚至可以动态修改类的定义或者新建一个原本没有的类!在各种框架中被广泛地使用,SpringAOP中使用的CGLib底层就是使用ASM来实现的。有兴趣可以查看官网:https://asm.ow2.io/之前我也写过一篇文章《Java用ASM写一个HelloWorld程序》,有兴趣可以看一下。

言归正传,如何通过ASM来获取参数名称呢?直接上代码:

[XML]纯文本查看复制代码

<dependency>

<groupId>asm</groupId>

<artifactId>asm</artifactId>

<version>3.3.1</version>

</dependency>

[Java]纯文本查看复制代码

/**

*使用字节码工具ASM来获取方法的参数名

*/

publicstaticString[]getMethodParamNames(finalMethodmethod)throwsIOException{

finalintmethodParameterCount=method.getParameterTypes().length;

finalString[]methodParametersNames=newString[methodParameterCount];

ClassReadercr=newClassReader(method.getDeclaringClass().getName());

ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);

cr.accept(newClassAdapter(cw){

@Override

publicMethodVisitorvisitMethod(intaccess,Stringname,Stringdesc,Stringsignature,String[]exceptions){

MethodVisitormv=super.visitMethod(access,name,desc,signature,exceptions);

finalType[]argTypes=Type.getArgumentTypes(desc);

//参数类型不一致

if(!method.getName().equals(name)||!matchTypes(argTypes,method.getParameterTypes())){

returnmv;

}

returnnewMethodAdapter(mv){

@Override

publicvoidvisitLocalVariable(Stringname,Stringdesc,Stringsignature,Labelstart,Labelend,intindex){

//如果是静态方法,第一个参数就是方法参数,非静态方法,则第一个参数是this,然后才是方法的参数

intmethodParameterIndex=Modifier.isStatic(method.getModifiers())?index:index-1;

if(0<=methodParameterIndex&&methodParameterIndex<methodParameterCount){

methodParametersNames[methodParameterIndex]=name;

}

super.visitLocalVariable(name,desc,signature,start,end,index);

}

};

}

},0);

returnmethodParametersNames;

}

/**

*比较参数是否一致

*/

privatestaticbooleanmatchTypes(Type[]types,Class<?>[]parameterTypes){

if(types.length!=parameterTypes.length){

returnfalse;

}

for(inti=0;i<types.length;i++){

if(!Type.getType(parameterTypes[i]).equals(types[i])){

returnfalse;

}

}

returntrue;

}

简而言之,ASM使用了访问者模式,它就像一个导游,带着我们去游览字节码文件中的各个“景点”。我们实现不同的Visitor接口就像是手上握有不同景点门票,导游会带着ClassVisitor去总体参观类定义的景观,而类内部有方法,如果你想看一下方法内部的定义,需要”额外购票”,即需要实现MethodVisitor才能跟着导游去参观方法定义这个景点。而在游览各个景点的时候,我们可以只游览我们感兴趣的部分,这就可以继承适配器(ClassAdapter和MethodAdapter分别是ClassVisitor和MethodVisitor的适配器)然后只实现我们感兴趣的方法即可。

这里对于类的定义,我们只对方法感兴趣,因此只实现visitMethod方法;在方法中,我们只对LocalVariableTable有兴趣,因此只实现visitLocalVariable方法。这样我们得到了局部变量表,再根据一些规则就可以拿到我们的参数名称了!是不是很棒!

顺便说一下,如果你使用maven来管理项目的话,这个-g参数会在编译的时候自动加上,因此我们不需要额外添加就可以通过字节码拿到,这也就是为什么SpringMVC可以拿到方法参数名称的原因。

但是这种方式对于接口和抽象方法是不管用的,因为抽象方法没有方法体,也就没有局部变量,自然也就没有局部变量表了:

MyBatis是通过接口跟SQL语句绑定然后生成代理类来实现的,因此它无法通过解析字节码来获取方法参数名。

关于导游网站源码分享,导游平台app的介绍到此结束,希望对大家有所帮助。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平