聊聊在获取方法参数名方面,Spring真的就比Mybatis强?

目录
  • 前言
  • 一、Spring是如何获取方法参数名称的?
    • Spring获取参数名称的两种方式
  • 二、Mybatis为什么没有向Spring学习?
    • Mybatis要获取的是接口方法的参数名称
  • 三、总结
  • 四、深入拓展
    • 1、从字节码说起
    • 2、看看普通类在不同参数编译下的.class字节码里面都有什么
    • 3、看看接口在不同参数编译下的.class字节码里面都有什么
  • 五、结束

前言

在使用 Spring MVC 写Controller的时候,即使不使用注解,只要参数名和请求参数的key对应上了,就能自动完成数值的封装。

但是在使用 Mybatis框架写接口方法向xml里的SQL语句传参时,必须使用@Param('')指定key值,在SQL中才可以取到。

Spring可以做到,难道Mybatis做不到吗?难道Mybatis技术不行?

一、Spring是如何获取方法参数名称的?

Spring框架自己写了一个工具类,用来专门获取方法参数名称,DefaultParameterNameDiscoverer类是一个聚合类,维护了一个LinkedList集合,里面的是真正处理的业务的类对象。

真正处理获取方法参数名称有三种情况:

  • 一种是处理Kotlin的情况(KotlinReflectionParameterNameDiscoverer)
  • 一种是通过java的反射方式获取(StandardReflectionParameterNameDiscoverer)
  • 最后是通过ASM字节码方式从LocalVariableTable中获取(LocalVariableTableParameterNameDiscoverer)
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
	  默认添加几个参数名称获取的工具
	public DefaultParameterNameDiscoverer() {
		if (KotlinDetector.isKotlinReflectPresent() && !GraalDetector.inImageCode()) {
			addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
		}
		/// add 会添加到一个 LinkedList 集合中,是一个有序的集合
		///  所以这里也就是按照优先级进行添加的
		 StandardReflectionParameterNameDiscoverer 这个类要求JDK1.8以上的版本,且编译要加上 -parameters 参数
		///  其实就是调用了  method.getParameters() 方法
		addDiscoverer(new StandardReflectionParameterNameDiscoverer());
		/// LocalVariableTableParameterNameDiscoverer 没有jdk版本要求,
		// 是通过ASM提供的通过字节码获取方法的参数名称
		// 但是依赖 javac 编译的时候 添加上 -g 参数
		addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
	}
}

Spring获取参数名称的两种方式

1、StandardReflectionParameterNameDiscoverer

这种是通过java的反射来后去参数名称的。但是通过反射获取方法参数名称是有前置条件的:

1、要求jdk8以上

2、编译的时候,必须加-parameters参数,例如:javac -parameters xxxx.java

只有满足以上条件编译出来的.class,才能通过反射获取到方法参数的名称。

2、LocalVariableTableParameterNameDiscoverer

这种是通过ASM框架,解析字节码文件来得到方法参数名称的。同样,此种方法也有前置条件:

1、编译的时候,必须添加-g参数,javac -g xxxx.java

两种方式对比

可以看出来,spring会先采用StandardReflectionParameterNameDiscoverer尝试去获取参数名称

如果获取不到就尝试通过LocalVariableTableParameterNameDiscoverer后去参数名称。

通过java反射获取参数名称的方法,前置条件比较严格,但是获取方式比较简单,直接通过反射的api调用就行,不依赖其他三方框架。

而ASM解析字节码方式的前置条件相对比较宽松,只需要编译的时候添加 -g参数就行,缺点就是依赖于ASM框架。(Spring已经把asm框架通过源码的形式加入到spring框架中了,所以不需要单独在去引用asm框架)

通常,我们

二、Mybatis为什么没有向Spring学习?

Mybatis要获取的是接口方法的参数名称

Mybatis团队难道不知道ASM技术吗?这个肯定不是。

其实真正原因在于,Mybatis框架需要获取的是接口的方法参数名称,而Spring需要获取的是类的方法的参数名称。这是类方法和接口方法是完全不一样的两个东西。

Java 要获取接口或者抽象方法的参数的名称,必须的是JDK8以上,而且编译的时候加上-parameters参数,只有这种情况下编译出来的.class才能获取到参数的名称。无论你是通过java反射还是asm字节码技术,前面两个条件必须同时满足。

所以当达到上述两个条件后,直接通过java的反射就可以直接获取参数名称,根本没必要通过asm技术去获取。

所以!Mybatis不是拿不到参数名称,而是必须要 jdk8 以上而且还得是-parameters编译才可以,当满足这两个条件的时候,你也可以不加@Param('')注解。

三、总结

最后总结一下:

SpringMVC也不是什么时候都可以在不借助注解的情况下获取到参数名称,完成自动绑定的,只不过是要到达的前置条件比较宽松,需要编译的时候添加-g参数,而-g这个参数一般编译的时候都会加上,因为有了这个参数,在生成的class文件中,添加具体的 line,source,vars 信息。在输出日志的时候,才能看到行号等信息,如果发生错误了,就很容易定位。一般在idea这种开发工具中运行代码的时候,都是默认有这个参数的。所以你再idea写测试代码的时候,通过asm总是能获取到方法的参数名称,及时没有设置-g参数,那是因为开发工具编译的时候,就默认给你添加了。

不信的话,你可以手动编译一个java文件,只通过javac 不添加任何参数,然后运行,asm框架也获取不到方法参数名称。

maven打包好像是默认都会加-g参数,所以你碰到的绝大多数情况,下通过asm都是可以获取到参数名称的,给人一种asm一定能获取参数名称的错觉。

所以在使用Spring框架的时候,一般不用加注解,通常情况下框架也是可以拿到参数名称的。

Mybatis,一般情况下都要加注解@Param,是因为,Mybatis需要获取的是接口的方法参数名称,要想拿到接口方法的参数名称的话,就必须在jdk8以上的环境下,而且编译必须加-parameters参数的时候才能获取到,这种情况一般生产环境下不会有-parameters参数,所以在写Mybatis的接口的时候最好加上@Param参数,以保证程序正常运行。

四、深入拓展

1、从字节码说起

java源文件经过java编译器编译为.class字节码文件,之后才能被JVM执行,所以我们可以获取到的信息都存在.class文件中,但是编译器编译.class的的文件默认不保留方法参数名。所以如果只是通过javac命令不添加任何参数情况下,编译出来的.class文件,是无论如何也不能获取到方法的参数名称的。

2、看看普通类在不同参数编译下的.class字节码里面都有什么

下面我们通过javac编译一个类,然后通过javap 看看里面的内容都有什么!

public class TestBean {
    public String myMethod(String helloWord){
        return "";
    }
}

我们分别用三种方式进行编译,然后通过javap -verbose TestBean.class 进行查看

javac TestBean.java
javac -g TestBean.java
javac -parameters TestBean.java

从上面截图可以看出来:

添加-g参数后,.class会多出LocalVariableTable的信息,里面可以看到有方法参数的名字“helloWord”。

添加-parameters参数后,.class会多出MethodParameters的信息,里面也可以看到有方法参数的名字“helloWord”。

而javac不加任何参数的话,则在任何地方都找不到方法参数名“helloword”的信息。

所以:也验证了前面所说的,默认情况下,无论是asm还是java反射都是无法获取方法参数名称的,因为字节码里面就没有参数名的信息。

但是如果是有-g参数编译的话,字节码里会在LocalVariableTable将方法的参数名称进行保存,而ASM框架是基于字节码技术的,所以是可以解析出参数名称的。但是java的反射API并未提供获取LocalVariableTable信息的内容,所以-g产生的信息,只能通过自己解析字节码获取,所以asm是可以做到的。

如果是有-parameters参数编译的话,字节码里面保存MethodParameters信息,里面就是方法参数名称的信息,java的反射api提供的接口可以直接获取到里面的信息。但是-parameters 是jdk8以后才提供的新特性,所以要想通过反射api获取的话,只能是jdk8及以上版本通过添加-parameters参数才可以。

3、看看接口在不同参数编译下的.class字节码里面都有什么

先写一个普通的接口

public interface TestInterfaceBean {
    public String testInterfaceMethod (String iName);
}

然后再用三种方式进行编译,最后通过javap -verbose TestInterfaceBean.class 进行查看

javac TestInterfaceBean.java
javac -g TestInterfaceBean.java
javac -parameters TestInterfaceBean.java

从上面三张截图可以看出来,只有 javac -parameters 编译出来的.class字节码文件才带有 接口方法参数名的信息,-g 编译出来的和默认的都是没有的。

所以嘛,对于接口而言,只有通过-parameters 编译的字节码才能有方法参数名称。这也就明白了为什么Mybatis的接口要加注解了吧,因为你如果不通过注解信息去获取的话,你不论是java反射也好,asm也好都拿不到参数名称,除非你编译的时候添加-parameters,而生产环境通常是不会加这个参数的。

五、结束

看到这里应该弄清楚,获取参数名称的套路了吧!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springmvc之获取参数的方法(必看)

    1.导包,四大核心包,一个切面包(AOP),logging,web,springmvc 2.配置文件,核心代码如下: web.xml <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param

  • 深入了解MyBatis参数

    深入了解MyBatis参数 相信很多人可能都遇到过下面这些异常: "Parameter 'xxx' not found. Available parameters are [...]" "Could not get property 'xxx' from xxxClass. Cause: "The expression 'xxx' evaluated to a null value." "Error evaluating expression '

  • spring与mybatis三种整合方法

    1.采用MapperScannerConfigurer,它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean. spring-mybatis.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3

  • Java 8中如何获取参数名称的方法示例

    前言 在Java 8之前的版本,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,这和动态语言严重依赖参数名称形成了鲜明对比.现在,Java 8开始在class文件中保留参数名,给反射带来了极大的便利. 示例: public class GetRuntimeParameterName { public void createUser(String name, int age, int version) { // } public static void main(Strin

  • 聊聊在获取方法参数名方面,Spring真的就比Mybatis强?

    目录 前言 一.Spring是如何获取方法参数名称的? Spring获取参数名称的两种方式 二.Mybatis为什么没有向Spring学习? Mybatis要获取的是接口方法的参数名称 三.总结 四.深入拓展 1.从字节码说起 2.看看普通类在不同参数编译下的.class字节码里面都有什么 3.看看接口在不同参数编译下的.class字节码里面都有什么 五.结束 前言 在使用 Spring MVC 写Controller的时候,即使不使用注解,只要参数名和请求参数的key对应上了,就能自动完成数值

  • Java获取代码中方法参数名信息的方法

    前言 大家都知道随着java8的使用,在相应的方法签名中增加了新的对象Parameter,用于表示特定的参数信息,通过它的getName可以获取相应的参数名.即像在代码中编写的,如命名为username,那么在前台进行传参时,即不需要再编写如@Parameter("username")类的注解,而直接就能进行按名映射. 如下的代码参考所示: public class T { private interface T2 { void method(String username, Stri

  • 使用spring通过aop获取方法参数和参数值

    目录 spring通过aop获取方法参数和参数值 自定义注解 切面 aop切面 注解.参数获取 1.定义需要切面的注解 2.在需要进行切面的方法标注注解 3.定义切面 spring通过aop获取方法参数和参数值 自定义注解 package com.xiaolc.aspect; import java.lang.annotation.*; /** * @author lc * @date 2019/9/10 */ @Documented @Target({ElementType.TYPE, Ele

  • SpringAOP切入点规范及获取方法参数的实现

    切入点规范 @Pointcut("execution(* com.example.server.service.TeacherService.*(..))") 上面的切入点会切入com.example.server.service.TeacherService下面的所有方法. 下面来详细介绍一下切入点表达式的规范. 1.execution():表达式主体. 2.第一个位置:表示返回类型, *号表示所有的类型. 3.第二个位置:表示需要拦截的包名.类名.方法名(方法参数). 需要注意的是

  • SpringAOP如何获取方法参数上的注解

    SpringAOP获取方法参数上的注解 一.示例 ① 如下代码,自定义一个参数注解@Test,并将其使用到方法参数上,用于标注需要检验的参数 /** * 自定义注解,用于参数 */ @Target(PARAMETER) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Test{ } /** * 接口层,使用使用@Test注解标记参数 */ @RestController @RequestMapping("/v1/

  • Android Studio 3.0被调方法参数名提示的取消方法

    android studio升级3.0之后, 代码中调用的方法增加了参数名提示: 很多人不习惯这个提示,我们来看看怎么取消这个提示: PS:下面看下Android Studio 3.0 gradle提示太老 解决方法 The android gradle plugin version 3.0.0-alpha1 is too old, please update to the latest version. To override this check from the command line

  • Spring Aop 如何获取参数名参数值

    前言: 有时候我们在用Spring Aop面向切面编程,需要获取连接点(JoinPoint)方法参数名.参数值. 环境: Mac OSX Intellij IDEA Spring Boot 2x Jdk 1.8x Code: package com.example.aopdemo.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.a

  • javascript获取URL参数与参数值的示例代码

    复制代码 代码如下: <script>                  function goto_url(url){                      var new_url = "http://shop.usteel.com/index.php?app=list_release";                      var d_date  = getParameter("date",url);                    

  • Java Spring Controller 获取请求参数的几种方法详解

    Java Spring Controller 获取请求参数的几种方法  1.直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交.若"Content-Type"="application/x-www-form-urlencoded",可用post提交 url形式:http://localhost:8080/SSMDemo/demo/addUser1?username=lixiaoxi&password=1

  • JavaScript获取function所有参数名的方法

    我写了一个 JavaScript函数来解析函数的参数名称, 代码如下: function getArgs(func) { // 先用正则匹配,取得符合参数模式的字符串. // 第一个分组是这个: ([^)]*) 非右括号的任意字符 var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1]; // 用逗号来分隔参数(arguments string). return args.split(",").map(functi

随机推荐