source

Java reflection을 사용하여 메서드 파라미터명을 얻을 수 있습니까?

gigabyte 2022. 11. 8. 21:07
반응형

Java reflection을 사용하여 메서드 파라미터명을 얻을 수 있습니까?

다음과 같은 수업이 있는 경우:

public class Whatever
{
  public void aMethod(int aParam);
}

사실을 알 수 있는 요?aMethod되지만 파라미터는 입니다.aParam, 그것은 타입입니다.int

Java 8에서는 다음 작업을 수행할 수 있습니다.

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

public final class Methods {

    public static List<String> getParameterNames(Method method) {
        Parameter[] parameters = method.getParameters();
        List<String> parameterNames = new ArrayList<>();

        for (Parameter parameter : parameters) {
            if(!parameter.isNamePresent()) {
                throw new IllegalArgumentException("Parameter names are not present!");
            }

            String parameterName = parameter.getName();
            parameterNames.add(parameterName);
        }

        return parameterNames;
    }

    private Methods(){}
}

당신의 에는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」Whatever수동 테스트를 수행할 수 있습니다.

import java.lang.reflect.Method;

public class ManualTest {
    public static void main(String[] args) {
        Method[] declaredMethods = Whatever.class.getDeclaredMethods();

        for (Method declaredMethod : declaredMethods) {
            if (declaredMethod.getName().equals("aMethod")) {
                System.out.println(Methods.getParameterNames(declaredMethod));
                break;
            }
        }
    }
}

.[aParam]-parametersJava 8 파 java java java java java java java java java java java java java 。

Maven 사용자의 경우:

<properties>
    <!-- PLUGIN VERSIONS -->
    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>

    <!-- OTHER PROPERTIES -->
    <java.version>1.8</java.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <!-- Original answer -->
                <compilerArgument>-parameters</compilerArgument>
                <!-- Or, if you use the plugin version >= 3.6.2 -->
                <parameters>true</parameters>
                <testCompilerArgument>-parameters</testCompilerArgument>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

자세한 내용은 다음 링크를 참조하십시오.

  1. 공식 Java 튜토리얼: 메서드 파라미터 이름 가져오기
  2. JEP 118: 실행 시 파라미터 이름에 대한 접근
  3. 파라미터 클래스의 Javadoc

요약:

  • 컴파일 중에 디버깅 정보가 포함되어 있는 경우 파라미터 이름을 취득할 수 있습니다.자세한 내용은 이 답변을 참조하십시오.
  • 그렇지 않으면 매개 변수 이름을 가져올 수 없습니다.
  • 할 수 사용method.getParameterTypes()

(코멘트 중 하나에서 설명한 바와 같이) 에디터의 자동 완성 기능을 쓰기 위해 몇 가지 옵션이 있습니다.

  • arg0,arg1,arg2syslog.
  • intParam,stringParam,objectTypeParam 등등.
  • 위의 조합 - 비고유형은 전자를, 원시형은 후자를 사용하세요.
  • 인수 이름을 전혀 표시하지 않고 유형만 표시합니다.

파라나머 도서관은 이 같은 문제를 해결하기 위해 만들어졌다.

몇 가지 다른 방법으로 메서드 이름을 결정하려고 합니다.클래스가 디버깅으로 컴파일된 경우 클래스의 바이트 코드를 읽어 정보를 추출할 수 있습니다.

다른 방법은 프라이빗 스태틱멤버가 컴파일된 후 jar에 배치되기 전에 클래스의 바이트코드에 삽입하는 것입니다.그런 다음 리플렉션을 사용하여 런타임에 클래스에서 이 정보를 추출합니다.

https://github.com/paul-hammant/paranamer

나는 이 도서관을 이용하는데 어려움을 겪었지만, 결국 작동하게 되었다.관리인에게 문제를 보고하고 싶습니다.

org.springframework.core를 참조하십시오.Default Parameter Name Discoverer 클래스

DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));

★★★.
코드는 Java 8 호환 컴파일러와 정식 파라미터 이름 저장 옵션(-parameters 옵션)을 사용하여 컴파일해야 합니다.
이 작동해야 .

Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
   System.err.println(m.getName());
   for (Parameter p : m.getParameters()) {
    System.err.println("  " + p.getName());
   }
}

java.beans 참조해 주세요.Constructor Properties는 이를 위해 설계된 주석입니다.

가능해서 봄 MVC 3에서 하는데, 정확히 어떻게 하는지 시간을 내지는 않았어요.

메서드 파라미터명과 URI 템플릿 변수명의 조회는 코드가 디버깅이 네이블로 컴파일 되어 있는 경우에만 실행할 수 있습니다.디버깅이 네이블로 되어 있지 않은 경우 변수 이름의 해결된 값을 메서드파라미터에 바인드하기 위해 @PathVariable 주석에서 URI 템플릿 변수 이름을 지정해야 합니다.예를 들어 다음과 같습니다.

스프링 매뉴얼에서 인용

다른 예시와 같이 가능하지 않지만 주석을 사용하여 매개 변수 이름을 이월하고 반사를 통해 해당 이름을 얻을 수 있습니다.

가장 깔끔한 해결책은 아니지만, 일을 해낼 수 있습니다.일부 웹 서비스는 실제로 매개 변수 이름을 유지하기 위해 이 작업을 수행합니다(즉, Glassfish를 사용하여 WS를 배포).

따라서 다음과 같은 작업을 수행할 수 있습니다.

Whatever.declaredMethods
        .find { it.name == 'aMethod' }
        .parameters
        .collect { "$it.type : $it.name" }

그러나 다음과 같은 목록을 얻을 수 있습니다.

["int : arg0"]

Groovy 2.5+에서 수정될 것으로 생각합니다.

현재 답은 다음과 같습니다.

  • 그루비 수업이라면 이름을 알 수 없지만 앞으로는 알 수 있을 거예요
  • Java 8에서 컴파일된 Java 클래스라면 가능합니다.

다음 항목도 참조하십시오.


모든 방법에 대해 다음과 같습니다.

Whatever.declaredMethods
        .findAll { !it.synthetic }
        .collect { method -> 
            println method
            method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
        }
        .each {
            println it
        }

이클립스를 사용하는 경우 아래 이미지를 참조하여 컴파일러가 메서드 파라미터에 대한 정보를 저장할 수 있도록 합니다.

여기에 이미지 설명 입력

사용된 인수의 이름을 알 수 없습니다.

단, 리플렉션으로 메서드시그니처를 취득하여 인수유형을 검출할 수 있습니다.getParameter 체크타입().

@Bozho가 말했듯이 컴파일 중에 디버깅 정보가 포함되어 있으면 가능합니다.여기 좋은 답이 있어요

객체의 생성자(반사)의 매개 변수 이름을 가져오려면 어떻게 해야 합니까?@AdamPaynter님

ASM 라이브러리를 사용합니다.나는 당신이 어떻게 목표를 달성할 수 있는지를 보여주는 사례를 제시하겠습니다.

먼저 이러한 의존관계를 가진 pom.xml부터 시작합니다.

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>5.2</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

그러면 이 수업은 네가 하고 싶은 것을 해야 해.정적 메서드를 호출하기만 하면 됩니다.getParameterNames().

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;

public class ArgumentReflection {
    /**
     * Returns a list containing one parameter name for each argument accepted
     * by the given constructor. If the class was compiled with debugging
     * symbols, the parameter names will match those provided in the Java source
     * code. Otherwise, a generic "arg" parameter name is generated ("arg0" for
     * the first argument, "arg1" for the second...).
     * 
     * This method relies on the constructor's class loader to locate the
     * bytecode resource that defined its class.
     * 
     * @param theMethod
     * @return
     * @throws IOException
     */
    public static List<String> getParameterNames(Method theMethod) throws IOException {
        Class<?> declaringClass = theMethod.getDeclaringClass();
        ClassLoader declaringClassLoader = declaringClass.getClassLoader();

        Type declaringType = Type.getType(declaringClass);
        String constructorDescriptor = Type.getMethodDescriptor(theMethod);
        String url = declaringType.getInternalName() + ".class";

        InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url);
        if (classFileInputStream == null) {
            throw new IllegalArgumentException(
                    "The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: "
                            + url + ")");
        }

        ClassNode classNode;
        try {
            classNode = new ClassNode();
            ClassReader classReader = new ClassReader(classFileInputStream);
            classReader.accept(classNode, 0);
        } finally {
            classFileInputStream.close();
        }

        @SuppressWarnings("unchecked")
        List<MethodNode> methods = classNode.methods;
        for (MethodNode method : methods) {
            if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) {
                Type[] argumentTypes = Type.getArgumentTypes(method.desc);
                List<String> parameterNames = new ArrayList<String>(argumentTypes.length);

                @SuppressWarnings("unchecked")
                List<LocalVariableNode> localVariables = method.localVariables;
                for (int i = 1; i <= argumentTypes.length; i++) {
                    // The first local variable actually represents the "this"
                    // object if the method is not static!
                    parameterNames.add(localVariables.get(i).name);
                }

                return parameterNames;
            }
        }

        return null;
    }
}

유닛 테스트의 예를 다음에 나타냅니다.

public class ArgumentReflectionTest {

    @Test
    public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {

        List<String> parameterNames = ArgumentReflection
                .getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
        assertEquals("firstName", parameterNames.get(0));
        assertEquals("lastName", parameterNames.get(1));
        assertEquals(2, parameterNames.size());

    }

    public static final class Clazz {

        public void callMe(String firstName, String lastName) {
        }

    }
}

GitHub에서 완전한 예를 찾을 수 있습니다.

주의사항

  • 원래 솔루션을 @AdamPaynter에서 약간 변경하여 Methods에서 사용할 수 있도록 했습니다.제가 제대로 이해했다면, 그의 솔루션은 오직 건설업자와만 작동합니다.
  • 방법은 does이 this this this this this this this this this this this this와 함께 사용할 수 .static 할 수 입니다.이 경우 ASM에 의해 반환되는 인수 수는 다르지만 쉽게 수정할 수 있기 때문입니다.

파라미터 이름은 컴파일러에만 도움이 됩니다.컴파일러가 클래스 파일을 생성할 때 매개 변수 이름은 포함되지 않습니다. 메서드의 인수 목록은 해당 인수 수와 유형으로만 구성됩니다.따라서 리플렉션(질문에 태그된 대로)을 사용하여 파라미터 이름을 검색할 수 없습니다.파라미터 이름은 어디에도 존재하지 않습니다.

그러나 리플렉션을 사용하는 것이 어려운 요건이 아닌 경우 소스 코드에서 직접 이 정보를 가져올 수 있습니다(사용자가 정보를 가지고 있다고 가정).

2센트를 더하면 javac -g를 사용하여 소스를 컴파일할 때 "디버깅을 위해" 클래스 파일에서 파라미터 정보를 사용할 수 있습니다.APT에서는 이용할 수 있지만 주석이 필요하기 때문에 사용할 수 없습니다.(4~5년 전에 비슷한 내용을 논의한 사람도 있습니다.http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )

요약하자면 소스 파일을 직접 작업하지 않으면 얻을 수 없습니다(컴파일 시 APT와 유사).

Java 바이트 코드에서 추가 기호 정보를 읽는 간단한 방법은 다음과 같습니다.

Reflector reflector = new Reflector();
JavaMethod method = reflector.reflect(Whatever.class)
    .getMethods()
    .stream()
    .filter(m -> "aMethod".equals(m.getName()))
    .findFirst()
    .get();
String paramName = method.getParameters().getVariables().get(0).getName();
System.out.println(paramName);

Maven Central 아티팩트:

<dependency>
    <groupId>com.intersult</groupId>
    <artifactId>coder</artifactId>
    <version>1.5</version>
</dependency>

언급URL : https://stackoverflow.com/questions/2237803/can-i-obtain-method-parameter-name-using-java-reflection

반응형