Frage

Ich habe eine Klasse, die XML und Reflexion verwendet Objects in einer anderen Klasse zurückzukehren.

Normalerweise sind diese Objekte Unterfelder eines externen Objekts, aber gelegentlich ist es etwas, was ich on the fly generieren möchten. Ich habe so etwas wie dies versucht, aber ohne Erfolg. Ich glaube, das ist, weil Java wird nicht zulassen, dass auf private Methoden zur Reflexion zugreifen zu 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 bereitgestellte Methode private ist, schlägt es mit einem NoSuchMethodException. Ich kann es lösen, indem sie die Methode public machen, oder eine andere Klasse zu machen abzuleiten aus.

Lange Rede kurzer Sinn, ich habe mich nur gefragt, ob es eine Möglichkeit war eine private Methode über Reflektion zugreifen zu können.

War es hilfreich?

Lösung

Sie können private Methode mit Reflektion aufrufen. Ändern des letzten Bit des entsandten Code:

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

Es gibt ein paar Einschränkungen. Zuerst getDeclaredMethod nur Methode in dem aktuellen Class erklärt finden, nicht von übergeordneten Typen geerbt. So durchläuft die konkrete Klassenhierarchie bei Bedarf auf. Zweitens kann ein SecurityManager Verwendung des setAccessible Verfahren verhindern. Also, es kann als PrivilegedAction arbeiten müssen (mit AccessController oder Subject).

Andere Tipps

getDeclaredMethod() Verwenden Sie ein eigenes Method-Objekt zu bekommen und dann method.setAccessible() verwenden, damit es tatsächlich nennen.

Wenn die Methode akzeptiert nicht-primitive Datentypen dann das folgende Verfahren verwendet werden können, eine private Methode jeder 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;
    }

Der Parameter akzeptiert sind obj, methoden und die Parameter. Zum Beispiel

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

Methode concatString kann als geltend gemacht werden

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

Sie können dies mit ReflectionTestUtils des Frühlings tun ( org.springframework.test.util.ReflectionTestUtils )

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

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

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

Lassen Sie mich vollständigen Code für die Ausführung geschützter Methoden über Reflexion bieten. Es unterstützt alle Arten von params einschließlich Generika, autoboxed params 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);
}

Methode verwendet Apache ClassUtils zur Überprüfung Kompatibilität von autoboxed params

Eine weitere Variante ist mit sehr leistungsfähiger joor Bibliothek https://github.com/jOOQ/jOOR

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

Es ermöglicht alle Felder wie endgültige statische Konstanten zu modifizieren und yn geschützten Methoden aufrufen, ohne konkrete Klasse in der Vererbungs hierarhy Angabe

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

Sie können mit Manifold des @Jailbreak für direkte, Typ -safe Java Reflexion:

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

public class Foo {
    private void callMe();
}

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

Ebenso können Sie die Jailbreak () Erweiterungsmethode für den einmaligen Gebrauch:

foo.jailbreak().callMe();

Durch die jailbreak() Methode, die Sie jedes Mitglied in Foo Hierarchie zugreifen können.

In beiden Fällen wird der Compiler den Methodenaufruf löst für Sie typ sicher, als ob eine öffentliche Methode, während Manifold für Sie unter der Haube eine effiziente Reflexion Code erzeugt.

Alternativ kann, wenn der Typ nicht statisch bekannt ist, können Sie Struktur Typing verwenden eine Schnittstelle eine Art erfüllen kann, ohne dass seine Umsetzung erklären zu definieren. Diese Strategie hält Typsicherheit und vermeidet Leistung und Identitätsfragen im Zusammenhang mit Reflexion und Proxy-Code.

Erfahren Sie mehr über Manifold .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top