由Spring的@RequestParam的name属性引发的一系列小事记

4 篇文章 0 订阅
1 篇文章 0 订阅

总结

为了不耽误看客的时间,文长图多也为了不让你搞混,这里先声明本文的总结要点,如下:

  • IDE不开启生成debugging info数据class字节码文件时,IDE原生编译启动不会为@RequestParam等HTTP请求注解生成name属性,即使用这些注解不标注name属性时,会报错。

  • 凡是Maven编译的class字节码文件,默认均有debugging info数据,所以项目即使不为@RequestParam注解标注name属性,也能正常使用。

  • Spring有两种策略来获取方法参数名称

    • JDK8或以上,通过编译选项-parameters,在class文件中写入可供原生Java API反射调用来获取方法参数名称的元数据
    • JDK7或以下,通过第三方工具包ASM [ASM项目主页],来解析class文件,获取方法参数名称。注意该工具需要开启通用的编译选项-g,表示生成调试信息到class文件
  • -parameters-g有区别,不是功能冗余的实现,-parameters使得原生Java API能读取class文件的方法参数名称,这在JDK7及之前是没办法用原生Java API实现的

  • 为了不乱了方向,提示一下,本文将出现Java原生JavacSpringBoot代码Maven使用,和IDEA开发工具共四者关系

说明

本文基本上不是为了解决问题,因为平常能遇到这样的情况,机会很小很小,更多的是希望能为你带来一些小小知识!多图慎入

起因

项目里面使用了SpringBoot(版本是2.0.0.M2,不是2.0.0.RELEASE),有一次在倒腾了开发软件IDEA之后,发现IDEA启动的项目运行失败了(记住这个IDEA启动~),原本正常的Controller接口的某个方法,突然在请求时,报出如下错误:

在这里插入图片描述
项目代码没动,就只是倒腾了IDEA,重装了一下,你就给我看这个?

经过

搜某度,得其解,仅需开启某配置项(generate debugging info,Eclipse也有对应的配置,名字不同)如下图所示即可(这配置模式的确是开启的,只是重装IDEA后发现是关的了~

在这里插入图片描述
按道理来说,项目能否正常,跟IDE是不能有关系的,确实,能出现这种问题,本身代码就是有问题了,经排查,使用SpringWeb的@PathVariable注解时,没有赋予name属性,即写法如下

在这里插入图片描述
相信有不少人在接触到一些教程的时候,看到了不少说name属性是可写可不写的,这个实际上不对,有Spring官方文档如下:

图片援引自Spring框架4.3.27.RELEASE的官方文档

文档说明了,在Spring能获取到方法参数名称的情况下,这个name就可以不写,Spring自动匹配。实际上不只是@PathVariable注解,Controller处理HTTP传参的注解如@RequestParam等都要注意,Spring源码中获取不到参数名称,而且又没有指定的情况下,就会在下图位置抛出异常

在这里插入图片描述
而实现了该接口的参数解析类,有如下图所示,这其实表示我们常用的@MatrixVariable@RequestHeader都是需要注意name属性给值的。

在这里插入图片描述

那如何判断注解里面的name加不加有没有影响呢?最简单的方法,直接反编译class文件,如果反编译后的java代码,你能看到参数名称是对的,那说明你的项目和配置里面,name属性基本可以不加的

Spring的文档写的是JDK8的时候可以通过参数-parameters来给字节码文件加上参数名称,实际上,这个参数是JDK8及之后才有的,难道JDK7就不行吗?JDK7也是可以的,用的参数-g,而且这个参数更加通用,是用来生成调试信息的。后面会聊到

经过X2

突然一想不太对,-parameters是JDK8及以上才有的参数,难道JDK7就不能自动解析方法参数名称了吗?

可以的!

两种方法变量名收集方式,ASM对比JDK8.

在这里插入图片描述

说明一下,java.lang.reflect.ExecutableJDK8及以上才新增的类,因此Spring通过这种方式来判断当前使用的JDK是哪个版本(仅判断是否是JDK8或以上)。但要注意一点,根据这段代码来看,是只有JDK8或以上版本时,才新增加Java reflect包的方法获取参数名称的数据,否则统一都是ASM工具包获取方法参数名称的,并不是说JDK8就不用ASM

为什么SpringBoot的这个方法要这样写呢?猜测如下:

  • SpringBoot的2.0.0.RELEASE或以上在POM默认加上了-parameters参数(限JDK8及以上使用),所以如果Maven编译时被去掉了-g选项,那么也可以通过这个-parameters来获取方法参数,即StandardReflect方式(参数加在哪里,后面会聊到)
  • ASM是通用获取方法参数名称的实现,不限制JDK版本,只需要编译时生成调试信息,因此该方法作为必选方案

经过X3

为什么Maven编译的,统统都行?

实践发现,只要是用maven编译了项目,那不管什么JDK版本,name属性加不加都行的!

很好解释,可以在maven的compile命令加上-X选项,表示输出debug信息,这时候就可以看到,maven编译的时候,默认是加上-g选项的,也就是默认生成调试信息,那这时候,Spring的ASM工具包就一定能获取到方法参数名称了。(但千万注意,Maven只是默认加上-g,要去掉还是可以的

在这里插入图片描述

经过X4

多余之举?

有个显微地方,估计使用SpringBoot的开发很少注意到,就是SpringBoot的2.0.0.RELEASE版本及以上,在引用的spring-boot-starter-parent-2.0.0.RELEASE.pom文件里面,有如下配置:
在这里插入图片描述

即SpringBoot的2.0.0.RELEASE版本开始,已经默认会在maven编译时加上-parameters了,那问题来了,JDK7不支持-parameters呀~,是的!SpringBoot的2.0.0.RELEASE也不支持JDK7了😭

在这里插入图片描述
但是这个并不是多余之举。考虑到Maven只是默认加上-g,也可以去掉该参数,这时候就可以发现,加上-parameters是个明智之举了!

经过X5

-parameters跟-g有啥子区别?

-parameters是JDK8及以后新增的编译器选项,根据JEP-118交付的功能,如下描述
在这里插入图片描述
而-g是javac各版本均有的编译器选项,-g是用来生成调试信息的,所以很明显,-parameters肯定是提供了一些新的功能实现,而这个功能实现就是原生Java API能正常通过reflect包的工具来读取class文件的方法参数名称,而调试信息却没办法

经过X6

idea迷惑行为之-g不识别,-g:vars就识别。在提出了前面的结论后,急需实践验证,为了方便,使用IDEA的编译来完成实践。有以下几点需要提前说明:

  • IDEA编译代码不是直接使用javac命令,而是用的Java Compiler API,但即使如此,也可以通过build.log来查看编译代码时候的参数
  • build.log在IDEA的软件目录下,与idea.log同一路径,如果是Mac用户,可以直接到如下路径

/Users/这里是用户名/Library/Logs/JetBrains/这里是IDEA目录/build-log
默认则可以通过菜单:帮助->Show Log in Finder/ Explorer,这个目录下能找到build-log

  • build.log默认是最低info级别日志,可修改build-log.properties的日志级别如下(修改后需要重启IDEA):
 log4j.rootLogger=debug, file
  • 关键性的编译参数日志在如下这一行,因此,实际上在tail该日志时,可用如下命令来快捷查看关键日志
tail -f build.log | grep --line-buffer Compiling
2020-07-09 10:44:34,914 [   1010]  DEBUG - s.incremental.java.JavaBuilder - 
Compiling chunk [debuggerinfotest] with options:
 "-deprecation -g:vars -encoding UTF-8 -source 8 -target 8 -s /Users/newcih/Programs/IdeaProjects/debuggerinfotest/target/generated-sources/annotations", 
 mode=in-process

IDEA的迷惑行为就在于,如果你添加了编译器选项为-g,那实际上编译时是不会使用的,而添加了-g:vars等等部分调试信息的选项,就能正常加入到编译中。

考虑到IDEA有generate debugging info的选项,可以猜测IDEA是把-g选项统一管控了,因此自定义加上-g或不加-g并不生效,而是只能通过该界面开关来控制

如何给IDEA添加编译选项?打开菜单:Preference -> 构建,执行,部署 -> 编译器 -> Java编译器即可,如下图:

在这里插入图片描述

结果

如果你顺利看到这里,没有被绕晕,那其实挺祝贺你的,因为这篇文章只是用来标记一些少见的情况,且基本是开发过程中才会遇到的🤣,但最重要的是,希望能给你带来一些开发方面的小小知识!

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

newcih

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值