Ich habe eine Klasse, die XML und Reflection verwendet, um Object
s 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.
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
).
Verwenden Sie getDeclaredMethod()
, um ein privates Methodenobjekt abzurufen, und verwenden Sie dann method.setAccessible()
, um es tatsächlich aufrufen zu können.
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");
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
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);
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>
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 .