개인 메소드를 호출하는 방법이 있습니까?


146

XML과 리플렉션을 사용하여을 Object다른 클래스 로 반환 하는 클래스가 있습니다.

일반적으로 이러한 객체는 외부 객체의 하위 필드이지만 때로는 생성하려는 객체입니다. 나는 이와 같은 것을 시도했지만 아무 소용이 없습니다. Java가 private리플렉션을위한 메소드에 액세스 할 수 없기 때문이라고 생각합니다 .

Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");

if ("SomeObject".equals(objectName))
    object = someObject;
else
    object = this;

method = object.getClass().getMethod(methodName, (Class[]) null);

제공된 방법이 private이면로 실패합니다 NoSuchMethodException. 메소드 public를 만들거나 다른 클래스를 만들어서 해결할 수 있습니다.

간단히 말해서, 나는 private반사를 통해 방법에 액세스 할 수있는 방법 이 있는지 궁금했습니다 .

답변:


303

리플렉션을 사용하여 개인 메서드를 호출 할 수 있습니다. 게시 된 코드의 마지막 비트 수정 :

Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

몇 가지 경고가 있습니다. 먼저, getDeclaredMethod현재 선언 된 메소드 만 찾고 Class, 슈퍼 타입에서는 상속되지 않습니다. 따라서 필요한 경우 구체적인 클래스 계층 구조를 탐색하십시오. 둘째, 방법의 SecurityManager사용을 막을 수 있습니다 setAccessible. 따라서 PrivilegedAction( AccessController또는 사용 Subject) 으로 실행해야 할 수도 있습니다 .


2
과거 에이 작업을 수행했을 때 메서드를 호출 한 후 method.setAccessible (false)을 호출했지만 이것이 필요한지 여부는 알 수 없습니다.
shsteimer

16
아니요, 접근성을 설정하면 해당 인스턴스에만 적용됩니다. 특정 Method 객체가 컨트롤에서 벗어나지 않는 한 안전합니다.
erickson

6
그렇다면 클래스 외부에서 호출 할 수 있다면 개인 메소드를 갖는 요점은 무엇입니까?
Peter Ajtai

2
또한 getDeclaredMethod()그냥 대신 호출하십시오 getMethod()-이것은 개인 메소드에서는 작동하지 않습니다.
Ercksen

1
@PeterAjtai 늦은 답변에 대해 죄송하지만 다음과 같이 생각하십시오. 오늘날 대부분의 사람들은 자물쇠가 사소하게 파손되거나 우회 될 수 있음을 알고 있지만 문을 잠급니다. 왜? 그것은 정직한 사람들을 정직하게 유지하는 데 도움이되기 때문입니다. private액세스가 비슷한 역할을 한다고 생각할 수 있습니다 .
erickson


25

메소드가 기본이 아닌 데이터 유형을 승인하면 다음 메소드를 사용하여 모든 클래스의 개인용 메소드를 호출 할 수 있습니다.

public static Object genericInvokeMethod(Object obj, String methodName,
            Object... params) {
        int paramCount = params.length;
        Method method;
        Object requiredObj = null;
        Class<?>[] classArray = new Class<?>[paramCount];
        for (int i = 0; i < paramCount; i++) {
            classArray[i] = params[i].getClass();
        }
        try {
            method = obj.getClass().getDeclaredMethod(methodName, classArray);
            method.setAccessible(true);
            requiredObj = method.invoke(obj, params);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return requiredObj;
    }

허용되는 매개 변수는 obj, methodName 및 매개 변수입니다. 예를 들어

public class Test {
private String concatString(String a, String b) {
    return (a+b);
}
}

concatString 메소드는 다음과 같이 호출 할 수 있습니다.

Test t = new Test();
    String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");

7
paramCount가 필요한 이유는 무엇 입니까? params.length 만 사용할 수 있습니까 ?
Saad Malik

7

Spring의 ReflectionTestUtils ( org.springframework.test.util.ReflectionTestUtils )를 사용 하여이 작업을 수행 할 수 있습니다

ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);

예 : 개인 메소드가있는 클래스가있는 경우 square(int x)

Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);

코어 스프링 에는 더 많은 ReflectionUtils 가 있습니다 .
Thunderforge

3

리플렉션을 통해 실행 보호 메소드에 대한 완전한 코드를 제공하겠습니다. 제네릭, 자동 상자 매개 변수 및 null 값을 포함한 모든 유형의 매개 변수를 지원합니다.

@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}

public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass(), instance, methodName, params);
}

@SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {

    Method[] allMethods = clazz.getDeclaredMethods();

    if (allMethods != null && allMethods.length > 0) {

        Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);

        for (Method method : allMethods) {
            String currentMethodName = method.getName();
            if (!currentMethodName.equals(methodName)) {
                continue;
            }
            Type[] pTypes = method.getParameterTypes();
            if (pTypes.length == paramClasses.length) {
                boolean goodMethod = true;
                int i = 0;
                for (Type pType : pTypes) {
                    if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
                        goodMethod = false;
                        break;
                    }
                }
                if (goodMethod) {
                    method.setAccessible(true);
                    return (T) method.invoke(instance, params);
                }
            }
        }

        throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
            Arrays.toString(paramClasses));
    }

    throw new MethodNotFoundException("There are no methods found with name " + methodName);
}

메소드는 자동 박스 매개 변수의 호환성을 확인하기 위해 아파치 ClassUtils를 사용합니다.


90000 회 이상의 조회수와 허용 된 답변으로 9 살짜리 질문에 대답 할 필요는 없습니다. 답이없는 질문에 답하십시오.
Anuraag Baishya

0

또 다른 변형은 매우 강력한 JOOR 라이브러리를 사용하는 것입니다 https://github.com/jOOQ/jOOR

MyObject myObject = new MyObject()
on(myObject).get("privateField");  

상속 정적 계층에 구체적인 클래스를 지정하지 않고 최종 정적 상수 및 필드를 보호하는 메소드를 호출 할 수 있습니다.

<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
     <groupId>org.jooq</groupId>
     <artifactId>joor-java-8</artifactId>
     <version>0.9.7</version>
</dependency>

0

직접적이고 타입이 안전한 Java 리플렉션을 위해 Manifold의 @Jailbreak 를 사용할 수 있습니다 .

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}

@Jailbreak의 계층 구조에 foo있는 모든 멤버에 직접 액세스 할 수 있도록 컴파일러에서 로컬 변수를 잠금 해제합니다 Foo.

마찬가지로 jailbreak () 확장 메소드를 일회용으로 사용할 수 있습니다.

foo.jailbreak().callMe();

jailbreak()방법을 통해 Foo의 계층 구조에 있는 모든 멤버에 액세스 할 수 있습니다 .

두 경우 모두, 컴파일러는 공개 메소드처럼 타입 안전하게 메소드 호출을 해결하는 반면, 매니 폴드는 효율적인 리플렉션 코드를 생성합니다.

또는 유형을 정적으로 알 수없는 경우 구조 입력 을 사용하여 구현을 선언하지 않고도 유형이 충족 할 수있는 인터페이스를 정의 할 수 있습니다. 이 전략은 형식 안전성을 유지하고 리플렉션 및 프록시 코드와 관련된 성능 및 ID 문제를 방지합니다.

매니 폴드 에 대해 자세히 알아보십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.