How Java Gets the name of a method parameter

Source: Internet
Author: User
Tags vars

In Java, you can get information about classes, fields, method signatures, and so on, such as the method name, return value type, parameter type, generic type parameter, and so on, but you cannot get the parameter name of the method. In a real-world development scenario, it is sometimes necessary to do something based on the parameter name of the method, such as @RequestParam, @PathVariable annotations like SPRING-MVC, if you do not specify the corresponding Value property, The default is to use the parameter name of the method as the parameter name of the HTTP request, how does it do it?

In this case, there are two ways to get the method to solve this requirement, the first method is to use annotations, specify the corresponding parameter names in the annotations, and when you need to use the parameter names, get the corresponding values in the annotations. The second method is to get the parameter name of the method from the bytecode, but there is a limitation that the class file generates the method parameter name information (in the local variable table localvariabletable) only when the debug information is generated using the-G or-g:vars parameter at compile time, and the-G : There is no method parameter name information in the class file compiled in the None way. Therefore, you cannot use this method if you want to completely independent of the class file's compilation mode. The implementation of the two approaches is discussed below.

First, from the annotations to get

Using annotations, we need to customize an annotation, specify the parameter name in the note, and then, through the reflection mechanism, get the annotations on the method parameters to get the corresponding annotation information. The custom annotation here is param, which specifies the parameter name through the value parameter, defines a tool class parameternameutils to get the list of parameter names for the specified method, Here is the list of parameter names for the method method1 defined in the test class Parameternametest, and the following is the specific code.

First define the annotations:

Package Com.mikan;import java.lang.annotation.*;/** * @author Mikan * @date 2015-08-04 23:39 */@Target ( Elementtype.parameter) @Retention (retentionpolicy.runtime) @Documentedpublic @interface Param {    String value ();}

Gets the tool class for parameter names in annotations:

Package Com.mikan;import java.lang.annotation.annotation;import java.lang.reflect.method;/** * @author Mikan * @date 2015-08-05 00:26 */public class Parameternameutils {/** * Gets the parameter name of the specified method * * @param method to get the parameter name * @re Turn list of parameter names by parameter order */public static string[] Getmethodparameternamesbyannotation (method method) {Annotation        [] parameterannotations = Method.getparameterannotations ();        if (parameterannotations = = NULL | | parameterannotations.length = = 0) {return null;        } string[] Parameternames = new String[parameterannotations.length];        int i = 0; For (annotation[] parameterannotation:parameterannotations) {for (Annotation Annotation:parameterannotatio                    N) {if (annotation instanceof Param) {Param Param = (Param) annotation;                parameternames[i++] = Param.value ();    }}} return parameternames; }}

Test class:

Package Com.mikan;import java.lang.reflect.method;import java.util.arrays;/** * @author Mikan * @date 2015-08-04 23:40 * * public class Parameternametest {public    void Method1 (@Param ("Parameter1") String param1, @Param ("Parameter2") String param2) {        System.out.println (param1 + param2);    }    public static void Main (string[] args) throws Exception {        class<parameternametest> clazz = Parameternametest.class;        method = Clazz.getdeclaredmethod ("Method1", String.class, String.class);        string[] Parameternames = Parameternameutils.getmethodparameternamesbyannotation (method);        System.out.println (arrays.tostring (Parameternames));}    }

Output Result:

[Parameter1, Parameter2]

Second, get from the class file

By default, Javac does not generate the local variable table information, only the row number table information, if you want to generate local variable table information, you need to specify the-G or-g:vars parameter, if you want to generate local variable table and line number table information, you can use the-g:vars,lines parameter. First look at the difference between the class file generated with the-G parameter and the-g:none parameter, and then discuss how to get it.

The test classes are as follows:

Package com.mikan;/** * @author Mikan * @date 2015-08-04 23:40 */public class ParameterNameTest1 {public    void method1 (String param1, String param2) {        System.out.println (param1 + param2);}    }

Use the-g parameter to generate debug information:

Javac-g-d/users/mikan/documents/workspace/project/algorithm/target/classes/users/mikan/documents/workspace/ Project/algorithm/src/main/java/com/mikan/*.java

View Bytecode by JAVAP:

Localhost:mikan mikan$ javap-c-v parameternametest1.classclassfile/users/mikan/documents/workspace/project/ Algorithm/target/classes/com/mikan/parameternametest1.class last modified 2015-8-5; Size 771 bytes MD5 checksum 1c7617df9da5106249ab744feae684d1 Compiled from ' Parameternametest1.java ' public class Com.mik An. ParameterNameTest1 sourcefile: "Parameternametest1.java" minor version:0 major version:51 Flags:acc_public, Acc_sup Erconstant Pool: #1 = methodref #9. #24//Java/lang/object. "   <init>:() V #2 = Fieldref #25. #26//Java/lang/system.out:ljava/io/printstream; #3 = Class #27//Java/lang/stringbuilder #4 = MethodRef #3. #24//java/lang/ StringBuilder. " <init>:() V #5 = MethodRef #3. #28//Java/lang/stringbuilder.append: (ljava/lang/string;) ljava/la   Ng/stringbuilder; #6 = MethodRef #3. #29//java/lang/stringbuilder.tostring: () ljava/lang/stRing             #7 = MethodRef #30. #31//JAVA/IO/PRINTSTREAM.PRINTLN: (ljava/lang/string;) V #8 = Class #32               Com/mikan/parameternametest1 #9 = Class #33//Java/lang/object #10 = Utf8 <init> #11 = Utf8 () V #12 = Utf8 Code #13 = Utf8 Linenumbert Able #14 = Utf8 localvariabletable #15 = Utf8 this #16 = Utf8 Lcom/mikan/para  MeterNameTest1; #17 = Utf8 method1 #18 = Utf8 (ljava/lang/string;  ljava/lang/string;) V #19 = Utf8 param1 #20 = Utf8 ljava/lang/string; #21 = Utf8 param2 #22 = Utf8 sourcefile #23 = Utf8 Parameternametest1.java #2   4 = Nameandtype #10: #11//"<init>":() V #25 = Class #34//Java/lang/system #26 = Nameandtype #35: #36//Out:ljava/io/printsTream; #27 = Utf8 Java/lang/stringbuilder #28 = nameandtype #37: #38//append: (ljava/lang/string;) L  Java/lang/stringbuilder;  #29 = Nameandtype #39: #40//toString: () ljava/lang/string; #30 = Class #41//Java/io/printstream #31 = Nameandtype #42: #43//println: (Ljav a/lang/string) V #32 = Utf8 com/mikan/parameternametest1 #33 = Utf8 Java/lang/object #34 =  Utf8 Java/lang/system #35 = Utf8 out #36 = Utf8 ljava/io/printstream;  #37 = Utf8 Append #38 = Utf8 (ljava/lang/string;) Ljava/lang/stringbuilder;  #39 = Utf8 toString #40 = Utf8 () ljava/lang/string; #41 = Utf8 Java/io/printstream #42 = Utf8 println #43 = Utf8 (Ljava/lang/strin    g;) v{public Com.mikan.ParameterNameTest1 (); Flags:acc_public code:stack=1, Locals=1, args_size=1 0:aload_0 1:invokespecial #1//Method java/lang/object. "  <init> ":() V 4:return linenumbertable:line 7:0 localvariabletable:start Length  Slot Name Signature 0 5 0 this lcom/mikan/parameternametest1;    public void Method1 (java.lang.String, java.lang.String); Flags:acc_public code:stack=3, locals=3, args_size=3 0:getstatic #2//Field java/         Lang/system.out:ljava/io/printstream;                  3:new #3//class Java/lang/stringbuilder 6:dup 7:invokespecial #4 Method Java/lang/stringbuilder. " <init> ":() V 10:aload_1 11:invokevirtual #5//Method Java/lang/stringbuilder.append        :(ljava/lang/string;) Ljava/lang/stringbuilder; 14:aload_2 15:invokevirtual #5//Method java/lang/stringbuilder.append: (ljava/lang/string;) ljava/lang/stringbuilder;        18:invokevirtual #6//Method java/lang/stringbuilder.tostring: () ljava/lang/string; 21:invokevirtual #7//Method java/io/printstream.println: (ljava/lang/string;) V 24:return L                Inenumbertable:line 10:0 line 11:24 localvariabletable:start Length Slot Name Signature               0 0 This lcom/mikan/parameternametest1;               0 1 param1 ljava/lang/string; 0 2 param2 ljava/lang/string;} Localhost:mikan mikan$
About the structure of the bytecode file and the meaning of each part of the representative, here is not a description, if necessary to view the relevant information.

The last part, from public void Method1 (java.lang.String, java.lang.String), is the bytecode information for the method method1, which can be seen from the last few lines:

      Localvariabletable:        Start  Length  Slot  Name   Signature               0     0   This Lcom/mikan/parameternametest1;               0     1 param1   ljava/lang/string;               0     2 param2   ljava/lang/string;
These lines represent the local variable table information, you can see the Method1 method has 3 parameters, why there are 3 parameters? Remember that in the instance method we can use this to represent the current instance, which is why, because at compile time the compiler automatically adds a parameter to us to represent the current instance, and it is the first parameter, and you can see PARAM1 and param2, which is the parameter name in the method declaration. Since there is information about the method parameter name in the bytecode, we can get the information from the class file in some way.

In addition, we can also note that we source System.out.println (param1 + param2); print two string parameter connections, at compile-time the compiler automatically to optimize the way to use StringBuilder, like this kind of compiler optimizations there are many ^_ ^.

The following is a look at the format of a class file that does not use the-G parameter or compiled with the-g:none parameter, which shows only the byte code of the method Method1, and the other omitted:

Localhost:mikan mikan$ javac-g:none-d/users/mikan/documents/workspace/project/algorithm/target/classes/users/ Mikan/documents/workspace/project/algorithm/src/main/java/com/mikan/*.javalocalhost:mikan mikan$ Javap-c-V    Parameternametest1.class public void Method1 (java.lang.String, java.lang.String); Flags:acc_public code:stack=3, locals=3, args_size=3 0:getstatic #2//Field java/         Lang/system.out:ljava/io/printstream;                  3:new #3//class Java/lang/stringbuilder 6:dup 7:invokespecial #4 Method Java/lang/stringbuilder. " <init> ":() V 10:aload_1 11:invokevirtual #5//Method Java/lang/stringbuilder.append        :(ljava/lang/string;) Ljava/lang/stringbuilder; 14:aload_2 15:invokevirtual #5//Method java/lang/stringbuilder.append: (ljava/lang/string;) Ljav        A/lang/stringbuilder;            18:invokevirtual #6      Method java/lang/stringbuilder.tostring: () ljava/lang/string; 21:invokevirtual #7//Method java/io/printstream.println: (ljava/lang/string;) V 24:return
It can be seen from the generated bytecode that the local variable table is no longer generated after using the-g:none parameter, so there is no information such as the parameter name of the method, so this is not the way to get the method parameter name, only the previous method can be used.

To get the parameter name information of the method from the bytecode, it is necessary to parse the Loadline code file, which requires that the bytecode file is familiar, which is a very complicated task. Fortunately we can use third-party class libraries like ASM, javassist to operate. Use asm4.0 here.

The code is as follows:

Package Com.mikan;import Org.objectweb.asm.*;import Java.io.ioexception;import java.io.inputstream;import Java.lang.reflect.method;import java.lang.reflect.modifier;import java.util.arrays;/** * @author Mikan * @date     2015-08-05 00:26 */public class Parameternameutils {/** * Gets the parameter name of the specified class specified method * * @param clazz the class to which the method to get the parameter name belongs * @param method to get the name of the parameter * @return the list of parameter names in the order of the parameters, or null if there are no parameters */public static string[] Getmethodparametern AMESBYASM4 (class<?> Clazz, final method) {final class<?>[] parametertypes = Method.getparameter        Types ();        if (parametertypes = = NULL | | parametertypes.length = = 0) {return null;        } final type[] types = new Type[parametertypes.length];        for (int i = 0; i < parametertypes.length; i++) {Types[i] = Type.GetType (Parametertypes[i]);        } final string[] parameternames = new String[parametertypes.length];       String className = Clazz.getname (); int lastdotindex = Classname.lastindexof (".");        ClassName = classname.substring (Lastdotindex + 1) + ". Class";        InputStream is = Clazz.getresourceasstream (className);            try {classreader Classreader = new Classreader (IS); Classreader.accept (New Classvisitor (OPCODES.ASM4) {@Override public methodvisitor Visitmeth                    OD (int access, string name, String desc, string signature, string[] exceptions) {//Only the specified method is processed                    type[] argumenttypes = type.getargumenttypes (DESC); if (!method.getname (). Equals (name) | |!                    Arrays.equals (Argumenttypes, types)) {return null; } return new Methodvisitor (OPCODES.ASM4) {@Override Publ                            IC void Visitlocalvariable (string name, String desc, string signature, label start, label end, int index) { The first parameter of a static method is the parameter of the method, if it is a realExample method, the first parameter is this if (Modifier.isstatic (Method.getmodifiers ())) {p                            Arameternames[index] = name;                            } else if (Index > 0) {parameternames[index-1] = name;                }                        }                    };        }}, 0);        } catch (IOException e) {e.printstacktrace ();    } return parameternames; }}

Test class:

Package Com.mikan;import java.lang.reflect.method;import java.util.arrays;/** * @author Mikan * @date 2015-08-04 23:40 * * public class Parameternametest {public    void Method1 (String param1, String param2) {        System.out.println (param1 + param2);    }    public static void Main (string[] args) throws Exception {        class<parameternametest> clazz = Parameternametest.class;        method = Clazz.getdeclaredmethod ("Method1", String.class, String.class);        string[] Parameternames = PARAMETERNAMEUTILS.GETMETHODPARAMETERNAMESBYASM4 (Clazz, method);        System.out.println (arrays.tostring (Parameternames));}    }
Output Result:

[Param1, Param2]


Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

How Java Gets the name of a method parameter

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.