it-swarm.com.de

Irgendeine Möglichkeit, eine private Methode aufzurufen?

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

Normalerweise handelt es sich bei diesen Objekten um Unterfelder eines externen Objekts, gelegentlich möchte ich jedoch spontan generieren. Ich habe so etwas probiert, aber ohne Erfolg. Ich glaube, das liegt daran, dass Java keinen Zugriff auf private-Methoden zur Reflektion zulässt.

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 bereitgestellte Methode private ist, schlägt sie mit einer NoSuchMethodException fehl. Ich könnte es lösen, indem ich die Methode public oder eine andere Klasse mache, aus der sie abgeleitet werden kann.

Um es kurz zu machen: Ich habe mich nur gefragt, ob es einen Weg gibt, auf eine private-Methode durch Reflektion zuzugreifen.

124
Sheldon Ross

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 einige Vorbehalte. Zunächst findet getDeclaredMethod nur die in der aktuellen Class deklarierte Methode, die nicht von Supertypen geerbt wird. Durchqueren Sie also gegebenenfalls die konkrete Klassenhierarchie. Zweitens kann eine SecurityManager die Verwendung der setAccessible-Methode verhindern. Daher muss es möglicherweise als PrivilegedAction ausgeführt werden (mit AccessController oder Subject).

259
erickson

Verwenden Sie getDeclaredMethod(), um ein privates Methodenobjekt abzurufen, und verwenden Sie dann method.setAccessible(), um es tatsächlich aufrufen zu können. 

33
Mihai Toader

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. Zum Beispiel 

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");
22
gKaur

Lassen Sie mich über Reflection vollständigen Code für die Ausführung geschützter Methoden bereitstellen. Es unterstützt alle Arten von Parametern, einschließlich Generics, autoboxed Parameter und Nullwerte

@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 zur Überprüfung der Kompatibilität von Autobox-Parametern

3
Pavel Chertalev

sie können dies mit ReflectionTestUtils of Spring tun ( org.springframework.test.util.ReflectionTestUtils )

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

Beispiel: Wenn Sie eine Klasse mit einer privaten Methode haben, square(int x)

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

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

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

Es ermöglicht das Ändern beliebiger Felder wie der endgültigen statischen Konstanten und das Aufrufen der geschützten Methoden, ohne dass eine konkrete Klasse in der Vererbungshierarhy 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
Pavel Chertalev

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

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

public class Foo {
    private void callMe();
}

@Jailbreak gibt die lokale Variable foo im Compiler für den direkten Zugriff auf alle Mitglieder in der Hierarchie von Foo frei.

Ebenso können Sie die Erweiterungsmethode jailbreak () zur einmaligen Verwendung verwenden:

foo.jailbreak().callMe();

Über die jailbreak()-Methode können Sie auf jedes Mitglied in der Hierarchie von Foo zugreifen.

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

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

Erfahren Sie mehr über Manifold .

0
Scott McKinney