Gibt es eine Möglichkeit, eine private Methode aufzurufen?


146

Ich habe eine Klasse, die XML und Reflection verwendet, um Objects an eine andere Klasse zurückzugeben.

Normalerweise sind diese Objekte Unterfelder eines externen Objekts, aber gelegentlich möchte ich sie im laufenden Betrieb generieren. Ich habe so etwas versucht, aber ohne Erfolg. Ich glaube, das liegt daran, dass Sie mit Java nicht auf privateMethoden zur Reflexion zugreifen können.

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);

Wenn die angegebene Methode ist private, schlägt sie mit a fehl NoSuchMethodException. Ich könnte es lösen, indem ich die Methode publicmache oder eine andere Klasse mache, von der ich sie ableiten kann.

Kurz gesagt, ich habe mich nur gefragt, ob es eine Möglichkeit gibt, privateüber Reflexion auf eine Methode zuzugreifen .

Antworten:


303

Sie können eine private Methode mit Reflektion aufrufen. Ändern des letzten Bits des veröffentlichten Codes:

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

Es gibt ein paar Einschränkungen. Erstens getDeclaredMethodwird nur die Methode gefunden, die in der aktuellen deklariert ist Classund nicht von Supertypen geerbt wurde. Durchlaufen Sie also gegebenenfalls die konkrete Klassenhierarchie. Zweitens SecurityManagerkann a die Verwendung der setAccessibleMethode verhindern. Daher muss es möglicherweise als PrivilegedAction(using AccessControlleroder Subject) ausgeführt werden.


2
Wenn ich dies in der Vergangenheit getan habe, habe ich nach dem Aufrufen der Methode auch method.setAccessible (false) aufgerufen, aber ich habe keine Ahnung, ob dies notwendig ist oder nicht.
Shsteimer

16
Nein, wenn Sie die Barrierefreiheit festlegen, gilt dies nur für diese Instanz. Solange Sie nicht zulassen, dass dieses bestimmte Methodenobjekt Ihrer Kontrolle entgeht, ist es sicher.
Erickson

6
Was bringt es also, private Methoden zu haben, wenn sie von außerhalb der Klasse aufgerufen werden können?
Peter Ajtai

2
Stellen Sie außerdem sicher, dass Sie getDeclaredMethod()statt nur aufrufen getMethod()- dies funktioniert nicht für private Methoden.
Ercksen

1
@PeterAjtai Entschuldigen Sie die verspätete Antwort, aber denken Sie so: Die meisten Menschen schließen heutzutage ihre Türen ab, obwohl sie wissen, dass das Schloss trivial aufgebrochen oder ganz umgangen werden kann. Warum? Weil es hilft, meist ehrliche Menschen ehrlich zu halten. Sie können sich vorstellen, dass der privateZugriff eine ähnliche Rolle spielt.
erickson


25

Wenn die Methode einen nicht primitiven Datentyp akzeptiert, kann die folgende Methode verwendet werden, um eine private Methode einer beliebigen Klasse aufzurufen:

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;
    }

Die akzeptierten Parameter sind obj, methodName und die Parameter. Beispielsweise

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

Die Methode concatString kann als aufgerufen werden

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

7
Warum wird paramCount benötigt? Können Sie nicht einfach params.length verwenden ?
Saad Malik


3

Lassen Sie mich vollständigen Code für ausführungsgeschützte Methoden per Reflektion bereitstellen. Es unterstützt alle Arten von Parametern, einschließlich Generika, Autobox-Parametern und Nullwerten

@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);
}

Die Methode verwendet Apache ClassUtils, um die Kompatibilität von Autobox-Parametern zu überprüfen


Es macht absolut keinen Sinn, eine 9 Jahre alte Frage mit mehr als 90000 Ansichten und einer akzeptierten Antwort zu beantworten. Beantworten Sie stattdessen unbeantwortete Fragen.
Anuraag Baishya

0

Eine weitere Variante ist die Verwendung einer sehr leistungsstarken JOOR-Bibliothek https://github.com/jOOQ/jOOR

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

Es ermöglicht das Ändern von Feldern wie endgültigen statischen Konstanten und das Aufrufen Ihrer geschützten Methoden, ohne dass in der Vererbungshierarchie eine konkrete Klasse angegeben wird

<!-- 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

Sie können Manifolds @Jailbreak für die direkte, typsichere Java-Reflexion verwenden:

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

public class Foo {
    private void callMe();
}

@JailbreakSchaltet die foolokale Variable im Compiler für den direkten Zugriff auf alle Mitglieder in Fooder Hierarchie frei.

Ebenso können Sie die Erweiterungsmethode jailbreak () für die einmalige Verwendung verwenden:

foo.jailbreak().callMe();

Über die jailbreak()Methode können Sie auf jedes Mitglied in Fooder Hierarchie zugreifen .

In beiden Fällen löst der Compiler den Methodenaufruf für Sie typsicher wie eine öffentliche Methode auf, während Manifold unter der Haube einen effizienten Reflektionscode für Sie generiert.

Wenn der Typ statisch nicht bekannt ist, können Sie alternativ mithilfe der strukturellen Typisierung eine Schnittstelle definieren, die ein Typ erfüllen kann, ohne seine Implementierung deklarieren zu müssen. Diese Strategie gewährleistet die Typensicherheit und vermeidet Leistungs- und Identitätsprobleme im Zusammenhang mit Reflection und Proxy-Code.

Erfahren Sie mehr über Manifold .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.