为何Spring MVC可获取到方法参数名,而MyBatis却不行?【享学Spring MVC】

每篇一句

胡适:多谈些问题,少聊些主义

前言

Spring MVCMyBatis作为当下最为流行的两个框架,大家平时开发中都在用。如果你往深了一步去思考,你应该会有这样的疑问:

  • 在使用Spring MVC的时候,你即使不使用注解,只要参数名和请求参数的key对应上了,就能自动完成数值的封装
  • 在使用MyBatis(接口模式)时,接口方法向xml里的SQL语句传参时,必须(当然不是100%的必须,特殊情况此处不做考虑)使用@Param(‘‘)指定key值,在SQL中才可以取到

我敢相信这绝不是我一个人的疑问,因为我在第一次使用MyBatis的时候就产生过这个疑问并且也尝试过去掉@Param注解,因为我觉得一个名称让我写两次是有点多此一举的(我太懒了)。
Spring MVC人性化处理比起来,当时觉得MyBatis对这块的处理简直弱爆了。费解了这么长时间,今天我终于可以解释这个现象了,来揭开它的面纱~

问题发现

java使用者都知道,.java文件属于源码文件,它需要经过了javac编译器编译为.class字节码文件才能被JVM执行的。
.class字节码稍微有点了解的小伙伴应该也知道这一点:Java在编译的时候对于方法,默认是不会保留方法参数名,因此如果我们在运行期想从.class字节码里直接拿到方法的参数名是做不到的。

如下案例,很明显就是获取不到真实参数名喽:

public static void main(String[] args) throws NoSuchMethodException {
    Method method = Main.class.getMethod("test1", String.class, Integer.class);
    int parameterCount = method.getParameterCount();
    Parameter[] parameters = method.getParameters();

    // 打印输出:
    System.out.println("方法参数总数:" + parameterCount);
    Arrays.stream(parameters).forEach(p -> System.out.println(p.getType() + "----" + p.getName()));
}

打印内容:

方法参数总数:2
class java.lang.String----arg0
class java.lang.Integer----arg1

从结果中可以看到我们并不能获取到真实方法参数名(获取到的是无意义的arg0、arg1等),这个结果符合我们的理论知识以及预期

若你有一定技术敏感性,这个时候你应该有这样的疑问:在使用Spring MVC的时候,Controller的方法中不使用注解一样可以自动封装啊,形如这样:

@GetMapping("/test")
public Object test(String name, Integer age) {
    String value = name + "---" + age;
    System.out.println(value);
    return value;
}

请求:/test?name=fsx&age=18。控制台输出:

fsx---18

从结果中可见:看似办不到的case,Spring MVC竟然给做到了(获取到了方法参数名,进而完成封装),是不是有点不可思议???

再看此例(还原Spring MVC获取参数名的场景):

public static void main(String[] args) throws NoSuchMethodException {
    Method method = Main.class.getMethod("test1", String.class, Integer.class);
    MethodParameter nameParameter = new MethodParameter(method, 0);
    MethodParameter ageParameter = new MethodParameter(method, 1);

    // 打印输出:
    // 使用Parameter输出
    Parameter nameOriginParameter = nameParameter.getParameter();
    Parameter ageOriginParameter = ageParameter.getParameter();
    System.out.println("===================源生Parameter结果=====================");
    System.out.println(nameOriginParameter.getType() + "----" + nameOriginParameter.getName());
    System.out.println(ageOriginParameter.getType() + "----" + ageOriginParameter.getName());
    System.out.println("===================MethodParameter结果=====================");
    System.out.println(nameParameter.getParameterType() + "----" + nameParameter.getParameterName());
    System.out.println(ageParameter.getParameterType() + "----" + ageParameter.getParameterName());
    System.out.println("==============设置上ParameterNameDiscoverer后MethodParameter结果===============");
    ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    nameParameter.initParameterNameDiscovery(parameterNameDiscoverer);
    ageParameter.initParameterNameDiscovery(parameterNameDiscoverer);
    System.out.println(nameParameter.getParameterType() + "----" + nameParameter.getParameterName());
    System.out.println(ageParameter.getParameterType() + "----" + ageParameter.getParameterName());
}

输出结果:

===================源生Parameter结果=====================
class java.lang.String----arg0
class java.lang.Integer----arg1
===================MethodParameter结果=====================
class java.lang.String----null
class java.lang.Integer----null
==============设置上ParameterNameDiscoverer后MethodParameter结果===============
class java.lang.String----name
class java.lang.Integer----age

从结果能看出来:Spring MVC借助ParameterNameDiscoverer完成了方法参数名的获取,进而完成数据封装。关于ParameterNameDiscoverer它的讲解,可先行参阅:【小家Spring】Spring标准处理组件大合集(ParameterNameDiscoverer、AutowireCandidateResolver、ResolvableType。。。)

该问介绍了ParameterNameDiscoverer的基本使用和提供的能力,但并没有深入分析。那么本文就分析为何Spring MVC为何可以正确的解析到方法参数名称这个问题,从字节码角度深入分析其缘由~



为了便于理解,先简单说说字节码中的两个概念:LocalVariableTableLineNumberTable。它哥俩经常被拿出来一起说,当然本文关注的焦点是LocalVariableTable,但也借此机会一笔带过LineNumberTable

LineNumberTable

你是否曾经疑问过:线上程序抛出异常时显示的行号,为啥就恰好就是你源码的那一行呢?有这疑问是因为JVM执行的是.class文件,而该文件的行和.java源文件的行肯定是对应不上的,为何行号却能在.java文件里对应上?
这就是LineNumberTable它的作用了:LineNumberTable属性存在于代码(字节码)属性中, 它建立了字节码偏移量到源代码行号之间的联系

LocalVariableTable

LocalVariableTable属性建立了方法中的局部变量与源代码中的局部变量之间的对应关系。这个属性也是存在于代码(字节码)中~
从名字可以看出来:它是局部变量的一个集合。描述了局部变量和描述符以及和源代码的对应关系

下面我使用javacjavap命令来演示一下这个情况:
.java源码如下:

package com.fsx.maintest;
public class MainTest2 {
    public String testArgName(String name,Integer age){
        return null;
    }
}

说明:源码我都是顶头写的,所以请注意行号~

使用javac MainTest2.java编译成.class字节码,然后使用javap -verbose MainTest2.class查看该字节码信息如下:

从图中可看到,我红色标注出的行号和源码处完全一样,这就解答了我们上面的行号对应的疑问了:LineNumberTable它记录着在源代码处的行号。
Tips:此处并没有,并没有,并没有LocalVariableTable

源码不变,我使用javac -g MainTest2.java来编译,再看看对应的字节码信息如下(注意和上面的区别):

这里多了一个LocalVariableTable,即局部变量表,就记录着我们方法入参的形参名字。既然记录着了,这样我们就可以通过分析字节码信息来得到这个名称了~

说明:javac的调试选项主要包含了三个子选项:lines,source,vars
如果不使用-g来编译,只保留源文件和行号信息;如果使用-g来编译那就都有了~

-parameters有什么区别??

知道-g编译参数的少,反倒对Java8新推出的-parameters知道的人更多一些。那么它和-g参数有什么区别呢???

百闻不如一见,我比较喜欢自己搞个例子来说明问题,.java源代码如下:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MainTest2 {

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = MainTest2.class.getMethod("testArgName", String.class, Integer.class);
        System.out.println("paramCount:" + method.getParameterCount());
        for (Parameter parameter : method.getParameters()) {
            System.out.println(parameter.getType().getName() + "-->" + parameter.getName());
        }
    }

    public String testArgName(String name, Integer age) {
        return null;
    }
}

下面分别用javac、javac -g、javac -parameters来编译后再执行,结果图如下:

从分别编译、再运行打印的结果图中看,结果以及他们的区别已经很清晰了,我就不再笔墨,有疑问的可以给我留言。

另外附上-parameters编译后的字节码信息,方便你做分析对比:


==获取方法参数名的3种方式介绍==

虽然Java编译器默认情况下会抹去方法的参数名,但有上面介绍了字节码的相关知识可知,我们还是有方法来得到方法的参数名的。下面介绍3个方案,供以参考。

方法一:使用-parameters

最为简单直接的方式,Java8源生支持:直接从java.lang.reflect.Parameter就能获取到,形如这样:

public class MainTest2 {

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = MainTest2.class.getMethod("testArgName", String.class, Integer.class);
        System.out.println("paramCount:" + method.getParameterCount());
        for (Parameter parameter : method.getParameters()) {
            System.out.println(parameter.getType().getName() + "-->" + parameter.getName());
        }
    }

    public String testArgName(String name, Integer age) {
        return null;
    }
}

输出:

paramCount:2
java.lang.String-->name
java.lang.Integer-->age

当然,它有两个最大的弊端:

  1. 必须Java8或以上(由于java8已经普及率非常高了,所以这个还好)
  2. 编译参数必须有-parameters(由于依赖编译参数,所以对迁移是不太友好的,这点比较致命)

指定-parameters编译参数的方式:

  1. 手动命令方式编译:javac -parameters XXX.java
  2. IDE(以Idea为例)编译:
  3. Maven编译:通过编译插件指定,保证项目迁移的正确性(推荐)
<!-- 编译环境在1.8编译 且附加编译参数:-parameters-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
        <compilerArgs>
            <arg>-parameters</arg>
        </compilerArgs>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <compilerVersion>${java.version}</compilerVersion>
        <encoding>${project.build.sourceEncoding}</encoding>
    </configuration>
</plugin>

优点:简单方便
缺点:需要特别的指定-parameters,不太方便(当然使用maven编辑插件来指定是相对靠谱的方案且推荐使用

方案二:使用-g + javap命令

如上例子可以使用javac -g编译后,再使用javap获取到字节码信息,然后自己根据信息的格式把参数名提取出来(自己做、自己做、自己做)

这无异于让你自己解析http协议一般,你愿意做吗???所以此办法虽为一种办法,但是显然不便采用

方案三:借助ASM(推荐)

说到ASM,小伙伴们至少对这个名字应该是不陌生的。它是一个Java字节码操控框架,它能被用来动态生成类或者增强既有类的功能,它能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

对于ASM来说,Java class被描述为一棵树;使用 “Visitor”模式(导游模式)遍历整个二进制结构;事件驱动的处理方式使得用户只需要关注于对其编程有意义的部分(比如本文只关注方法参数,其它的不关心),而不必了解 Java 类文件格式的所有细节

----

ASM方式,它仍旧还是基于编译后的字节码做事的,正所谓巧妇难为无米之炊,所以它仍旧必须依赖于编译时的LocalVariableTable(-g 参数)。

你可能会发问:我使用idea编译/maven编译都没有自己去指定-g参数啊,为什么也好使呢?你的疑问同样也是我的疑问,我至今也还没弄清楚更根本的原因,但是我可以说如下两个现象

  1. idea默认使用的是javac编译器,编译出来的字节码是带有LocalVariableTable的。但你也可以关闭它,如下图:
  2. maven默认使用的也是javac编译,字节码也带有LocalVariableTable(但是maven编译时候的编译命令、参数等,我无法获知。恳请精通maven的同学指点~)

----

小插曲:关于代理的科普(Proxy、CGLIB、Javassist、ASM ):

  1. ASMJava字节码开源操控框架。操纵的级别是底层JVM的汇编指令级别,这就要求使用者对class组织结构和JVM汇编指令有一定的了解,要求颇高。
  2. Javassist:效果同上。相较于ASM它的特点是操作简单,并且速度还可以(当然没有ASM快)。重要的是:它并不要求你了解JVM指令/汇编指令~
  3. Proxy动态代理:动态生成(非提前编译好)代理类:$Proxy0 extends Proxy implements MyInterface{ ... },这就决定了它只能对接口(或者实现接口的类)进行代理,单继承机制也决定了它不能对(抽象)类进行代理~
  4. CGLIB:是一个基于ASM的强大的,高性能,高质量的字节码生成库。它可以在运行期扩展Java类与实现Java接口。

    > Spring AOP以及Hibernate对代理对象的创建中都使用了CGLIB

前面文章有介绍过了直接使用CGLIBAPI来操作字节码/生成代理对象,本文将简单演示一下直接使用ASM框架来操作的示例:

ASM使用示例

首先导入asm依赖包:

<!-- https://mvnrepository.com/artifact/asm/asm -->
<dependency>
    <groupId>asm</groupId>
    <artifactId>asm</artifactId>
    <version>3.3.1</version>
</dependency>

说明:asm现已升级到7.x版本了,并且GAV已变化。由于我对3.x熟悉点,所以此处我还是守旧吧~

基于ASM提供工具方法getMethodParamNames(Method),获取到任何一个Method的入参名称:

public class MainTest2 {

    // 拿到指定的Method的入参名们(返回数组,按照顺序返回)
    public static String[] getMethodParamNames(Method method) throws IOException {
        String methodName = method.getName();
        Class<?>[] methodParameterTypes = method.getParameterTypes();
        int methodParameterCount = methodParameterTypes.length;
        String className = method.getDeclaringClass().getName();
        boolean isStatic = Modifier.isStatic(method.getModifiers());
        String[] methodParametersNames = new String[methodParameterCount];

        // 使用org.objectweb.asm.ClassReader来读取到此方法
        ClassReader cr = new ClassReader(className);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        // 这一步是最红要的,开始visitor浏览了
        // ClassAdapter是org.objectweb.asm.ClassVisitor的子类~~~~
        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);

                // 只visitor方法名相同和参数类型都相同的方法~~~
                if (!methodName.equals(name) || !matchTypes(argTypes, methodParameterTypes)) {
                    return mv;
                }

                // 构造一个MethodVisitor返回 重写我们关心的方法visitLocalVariable~~~
                return new MethodAdapter(mv) {

                    //特别注意:如果是静态方法,第一个参数就是方法参数,非静态方法,则第一个参数是 this ,然后才是方法的参数
                    @Override
                    public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
                        // 处理静态方法与否~~
                        int methodParameterIndex = isStatic ? 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<?>[] 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;
    }
}

运行案例:

public class MainTest2 {

    // 使用工具方法获取Method的入参名字~~~
    public static void main(String[] args) throws SecurityException, NoSuchMethodException, IOException {
        Method method = MainTest2.class.getDeclaredMethod("testArgName", String.class, Integer.class);
        String[] methodParamNames = getMethodParamNames(method);

        // 打印输出
        System.out.println(StringUtils.arrayToCommaDelimitedString(methodParamNames));
    }

    private String testArgName(String name, Integer age) {
        return null;
    }
}

输出:

name,age

效果复合预期,使用ASM拿到了我们期望的真实的方法参数名(没有指定任何编译参数哦)。使用基于ASM的方式,即使你是Java8以下的版本,都是能够正常获取到的,因为它并不依赖编译参数~~~

==有了这些基础知识,那么书归正传,来解释文首的疑问:==

Spring MVC为何好使?

首先使用上需明确Spring MVC好使但它并不依赖于-parameters参数,也不依赖于-g这个编译参数,因为它是借助ASM来实现的~

spring-core中有个ParameterNameDiscoverer就是用来获取参数名的,底层用的是asm解析,但是接口方法的参数名无法得到,即只能是非接口类的方法参数名可以。
从文首的例子可以看出Spring MVC它最终依赖的是DefaultParameterNameDiscoverer去帮忙获取到入参名,看看这块代码:

// @since 4.0
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {

    public DefaultParameterNameDiscoverer() {
        if (!GraalDetector.inImageCode()) {
            if (KotlinDetector.isKotlinReflectPresent()) {
                addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
            }
            addDiscoverer(new StandardReflectionParameterNameDiscoverer());
            addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
        }
    }
}

DefaultParameterNameDiscoverer它就是一个责任链模式的体现,靠它添加进来的实现类来处理,也就是这哥俩:
StandardReflectionParameterNameDiscoverer:依赖于-parameters才会有效(有java版本要求和编译参数要求)
LocalVariableTableParameterNameDiscoverer:基于ASM实现,无版本和编译参数要求~

备注:Spring使用ASM无需额外导包,因为自给自足了:

MyBatis为何不好使?

首先使用上需要明确这一点:MyBatis通过接口跟SQL语句绑定然后生成代理类来实现的。

既然有了强大的ASM,那么问题来了:难道ASM也帮不到MyBatis来简化开发?
看看我给的这个例子或许你就能明白了并不能怪MyBatis呀:

public class MainTest2 {

    // 使用工具方法获取Method的入参名字~~~
    public static void main(String[] args) throws SecurityException, NoSuchMethodException, IOException {
        Method method = MainTest2.class.getDeclaredMethod("testArgName", String.class, Integer.class);
        String[] methodParamNames = getMethodParamNames(method);

        // 打印输出
        System.out.println(StringUtils.arrayToCommaDelimitedString(methodParamNames));
    }
}

// 接口方法
interface MyDemoInterface{
    String testArgName(String name, Integer age);
}

输出:

null,null

可见即使强如ASM,也是木有办法直接获取到接口的形参名的。
这是可以被理解的,因为接口方法不是实际方法,它的形参名是会被实现类覆盖的,所以接口方法的形参名意义不大~

Tips:接口上的default方法和static方法的参数名是可以被正常获取到的,有兴趣的小伙伴可以自己动手试试~

至于ASM为何对接口无效,其根本原因我展示一下字节码一看便清楚了:

因为抽象方法没有方法体,也就没有局部变量,自然也就没有局部变量表了,所以即使使用ASM也拿不到它的变量名~

说明:在Java8后使用-parameter参数即使是接口,是可以直接通过Method获取到入参名的,这个对MyBatis是好用的。当然为了保证兼容性,个人建议还是乖乖使用@Param注解来指定吧~

至此,我有理由相信小伙伴是和我一样,彻底搞明白为何Spring MVC可以,但MyBatis却不可以这个疑问了吧~~~

总结

本文深入到字节码处分析了这个有可能也是困扰了你很久的问题(问题如题),希望为你答疑解惑了。同时也介绍了ASM的基本用法,或许对你后续理解别的框架会有所帮助~

== 若对Spring、SpringBoot、MyBatis等源码分析感兴趣,可加我wx:fsx641385712,手动邀请你入群一起飞 ==
== 若对Spring、SpringBoot、MyBatis等源码分析感兴趣,可加我wx:fsx641385712,手动邀请你入群一起飞 ==

原文地址:https://www.cnblogs.com/fangshixiang/p/11532689.html

时间: 2024-10-11 00:23:43

为何Spring MVC可获取到方法参数名,而MyBatis却不行?【享学Spring MVC】的相关文章

为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】

每篇一句 你应该思考:为什么往往完成比完美更重要? 前言 在Spring Cloud微服务应用体系中,远程调用都应负载均衡.我们在使用RestTemplate作为远程调用客户端的时候,开启负载均衡极其简单:一个@LoadBalanced注解就搞定了. 相信大家大都使用过Ribbon做Client端的负载均衡,也许你有和我一样的感受:Ribbon虽强大但不是特别的好用.我研究了一番,其实根源还是我们对它内部的原理不够了解,导致对一些现象无法给出合理解释,同时也影响了我们对它的定制和扩展.本文就针对

JAVA获取方法参数名的分析(一)

关于题目 首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数.但有一个东西反射取不到,那就是我们对方法传入参数的命名. 取得传入参数的名字有什么意义? 对这个问题的探究,源于在写一个测试类时候的需求.假设我们有一个类需要测试,这个类中有数十个方法.为每个方法编写测试类,将耗费大量的时间和精力.因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数

java如何获取方法参数名

在java中,可以通过反射获取到类.字段.方法签名等相关的信息,像方法名.返回值类型.参数类型.泛型类型参数等,但是不能够获取方法的参数名.在实际开发场景中,有时需要根据方法的参数名做一些操作,比如像spring-mvc中,@RequestParam.@PathVariable注解,如果不指定相应的value属性,默认就是使用方法的参数名做为HTTP请求的参数名,它是怎么做到的呢? 在这样情况下,有两种方法获取方法来解决这种需求,第一种方法是使用注解,在注解中指定对应应的参数名称,在需要使用参数

HandlerMethodArgumentResolver(一):Controller方法入参自动封装器(将参数parameter解析为值)【享学Spring MVC】

HandlerMethodArgumentResolver(一):Controller方法入参自动封装器(将参数parameter解析为值)[享学Spring MVC] 原文地址:https://www.cnblogs.com/hfultrastrong/p/12618488.html

RestTemplate相关组件:ClientHttpRequestInterceptor【享学Spring MVC】

每篇一句 做事的人和做梦的人最大的区别就是行动力 前言 本文为深入了解Spring提供的Rest调用客户端RestTemplate开山,对它相关的一些组件做讲解. Tips:请注意区分RestTemplate和RedisTemplate哦~ ClientHttpRequestFactory 它是个函数式接口,用于根据URI和HttpMethod创建出一个ClientHttpRequest来发送请求~ ClientHttpRequest它代表请求的客户端,该接口继承自HttpRequest.Htt

RestTemplate的使用和原理你都烂熟于胸了吗?【享学Spring MVC】

每篇一句 人圆月圆心圆,人和家和国和---中秋节快乐 前言 在阅读本篇之前,建议先阅读开山篇效果更佳.RestTemplate是Spring提供的用于访问Rest服务的客户端工具,它提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率. 弱弱呼吁一句:对于那些在Spring环境下还在使用HttpClient(或其它Client)的同学,今儿看完本文后,建议切换到RestTemplate (有特殊需求的当然除外喽~). RestTemplate简化了与http服务的通信,程序代码

@Qualifier高级应用---按类别批量依赖注入【享学Spring】

每篇一句 罗斯:选秀状元可能有水货,但MVP绝对没有 前言 在上篇文章(讲解@LoadBalanced负载均衡)的末尾,我抛出了一个很重要的问题,建议小伙伴自己深入思考一番:本文主要针对此问题,作出一个统一的答复和讲解. 由于本人觉得这块知识点它属于Spring Framework的核心内容之一,非常的重要,因此单拎出来作专文讲述,希望对你有所帮助. 背景案例 说到@Qualifier这个注解大家并不陌生:它用于"精确匹配"Bean,一般用于同一类型的Bean有多个不同实例的case下

MVC – 6.Controller Action方法参数与返回值

6.1 Controller接收浏览器数据 a.获取Get数据 : a1:获取路由url中配置好的制定参数: 如配置好的路由: 浏览器请求路径为: /User/Modify/1 ,MVC框架获取请求后,就会找到匹配的路由映射路径url,得知是请求的控制器类 User里的Modify方法,此时就会检查此方法是否包含一个名为 id 的参数,如果有,按照配置的url 获取"参数"{id},并传给此方法. a2.直接通过请求上下文对象里的 Request获取url ?后的的参数: 浏览器请求路

javassist:增强型的java反射工具,获取方法参数名

java的反射是不能获取方法的参数名的.比如: [java] view plaincopyprint? public String concatString(String str1,String str2){ return str1+str2; }     public String concatString(String str1,String str2){         return str1+str2;     } 想获取"str1",和"str1"这个参数