How Java Gets the name of a method parameter

Source: Internet
Author: User
Tags instance method modifier vars

In Java, you can get information about classes, fields, method signatures, and so on by reflection, such as method names, return value types, parameter types, generic type parameters, and so on, but you cannot get the parameter names of methods. In the actual development scenario, it is sometimes necessary to do something based on the parameter name of the method, such as Spring-mvc, @RequestParam, @PathVariable annotation, 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 the annotation, specify the corresponding parameter name in the note, and when you need to use the parameter name, get the corresponding value in the note. The second method is to get the parameter name of the method from the bytecode, but there is a limit to the method parameter name information (in the local variable table localvariabletable) that is generated only when the debug information is generated by using the-G or-g:vars parameter at compile time, and using the-G : None is compiled in a class file with no method parameter name information. So if you want to completely do not rely on the class file compilation mode, you cannot use this method. The following two implementations are discussed.

first, get from the annotation

Using the annotation method, we need to customize a note, specify the parameter name in the annotation, and then get the annotation on the method parameter through the reflection mechanism to obtain the corresponding annotation information. The custom annotation here is param, specifying the parameter name with the value parameter, and defining a tool class Parameternameutils to get the list of parameter names for the specified method. This gets the list of parameter names for the method method1 defined in the test class Parameternametest, and the following is the specific code.

To define the annotations first, note that the target of the annotation is "parameters" and the retention policy is "run-time" because the annotation information needs to be obtained at run time:

Package Com.mikan;

Import java.lang.annotation.*;

/**
 * @author Mikan
 * @date 2015-08-04 23:39
 /
@Target (elementtype.parameter)
@Retention ( Retentionpolicy.runtime)
@Documented public
@interface Param {
    String value ();
}

Get the tool class for the parameter name in the note:

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 name of the parameter * @return the list of parameter names in the order of parameters/public static string[] Getmethodparameternamesbyannot
        Ation (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:parameterannotati
                    ON) {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 method = Clazz.getdeclaredmethod ("Method1", String.class, String.class);
        string[] Parameternames = Parameternameutils.getmethodparameternamesbyannotation (method);
        System.out.println (arrays.tostring (parameternames));
    }


Output results:

[Parameter1, Parameter2]

second, from the class file to obtain

By default, Javac does not generate local variable table information, only a row number table information, if you want to generate local variable table information, you need to specify-G or-g:vars parameters, if you want to generate local variables table and row number table information, you can use the-g:vars,lines parameter. First look at the difference between the class file generated using 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);
    }

To generate debugging information using the-G parameter:

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

To view a byte code by JAVAP:

Localhost:mikan mikan$ javap-c-v parameternametest1.class classfile/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. Mikan. ParameterNameTest1 sourcefile: "Parameternametest1.java" minor version:0 major version:51 Flags:acc_public, ACC _super Constant 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/l
   Ang/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 #3               2//Com/mikan/parameternametest1 #9 = Class #33//Java/lang/object #10 = Utf8 <init> #11 = Utf8 () V #12 = Utf8 Code #13 = Utf8 Linen Umbertable #14 = Utf8 localvariabletable #15 = Utf8 this #16 = Utf8 lcom/m
  Ikan/parameternametest1; #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 #24 = Nameandtype #10: #11//"<init>":() V #25 = Class #34//Java/lang/sy Stem #26 = Nameandtype #35: #36//Out:ljava/io/printstream; #27 = Utf8 Java/lang/stringbuilder #28 = nameandtype #37: #38//append: (ljava/lang/string;)
  Ljava/lang/stringbuilder;
  #29 = Nameandtype #39: #40//toString: () ljava/lang/string; #30 = Class #41//Java/io/printstream #31 = Nameandtype #42: #43//println: (Lja va/lang/string V #32 = Utf8 com/mikan/parameternametest1 #33 = Utf8 java/lang/object #3
  4 = 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/str
ing;) 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 Len

  Gth 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 ja
         Va/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.appe
        Nd: (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 Linenumbertable:line 10:0 line 11:24 localvariabletable:start Length Slot Name Sig
               Nature 0-0 this lcom/mikan/parameternametest1;
               0 1 param1 ljava/lang/string;
0 2 param2 ljava/lang/string; } Localhost:mikan mikan$
With regard to the structure of bytecode files and the meaning of each part, here is not a description, if necessary to see the relevant information.

The last part, from the public void Method1 (java.lang.String, java.lang.String), is the byte-code information for 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 local variable table information, and you can see that the Method1 method has 3 parameters and why there are 3 parameters. Remember in the instance method that we can use this to represent the current instance? That's why, because at compile time the compiler automatically adds a parameter to the current instance, and it's the first argument, and you see PARAM1 and param2, which is the name of the parameter in the method declaration. Since the byte code has the method parameter name information, then we can obtain this information in some way from the class file.

In addition, we can note that our source code in 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 similar compiler optimization there are many ^_ ^.

Here's a look at the format of a class file that does not use the-G parameter or compiled with the-g:none parameter, where only the byte code of the method method1 is displayed, and the others are 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/*.java Localhost: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 ja
         Va/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.appe
        Nd: (ljava/lang/string;) Ljava/lang/stringbuilder; 14:aload_2 15:invokevirtual #5//Method Java/lang/stringbuilder.append: (ljava/lang/string;) Lja
        Va/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 it is not possible to get the method parameter name in this case, only the former method can be used.

To get the parameter name information of the method from the bytecode, you need to parse the codewords code file, which requires that you are familiar with the bytecode file, which is a very complicated task. Fortunately we can use a third party class library 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 {/** * Get the name of the parameter for the specified method of the class * * @param clazz to get the name of the class * @param method to get the name of the parameter * @return the list of parameter names in the order of parameters, if there are no arguments, return null * * p Ublic Static string[] GETMETHODPARAMETERNAMESBYASM4 (class<?> Clazz, Final method method) {final class<?& Gt
        [] parametertypes = Method.getparametertypes ();
        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 Visitme
                    Thod (int access, string name, String desc, string signature, string[] exceptions) {//handling only specified methods
                    type[] argumenttypes = type.getargumenttypes (DESC); if (!method.getname (). Equals (name) | |!
                    Arrays.equals (Argumenttypes, types)) {return null; return new Methodvisitor (OPCODES.ASM4) {@Override P
 ublic 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, and if it is an instance method, the first argument is this if (modifier.isstatic)
                            . GetModifiers ()) {Parameternames[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 method = Clazz.getdeclaredmethod ("Method1", String.class, String.class);
        string[] Parameternames = PARAMETERNAMEUTILS.GETMETHODPARAMETERNAMESBYASM4 (Clazz, method);
        System.out.println (arrays.tostring (parameternames));
    }

Output results:

[Param1, Param2]


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.