java - usar - ¿Alguna forma de invocar un método privado?
llamar a un objeto de otra clase java (6)
Tengo una clase que usa XML y reflexión para devolver Object
s a otra clase.
Normalmente, estos objetos son subcampos de un objeto externo, pero ocasionalmente es algo que quiero generar sobre la marcha. He intentado algo como esto, pero fue en vano. Creo que es porque Java no le permitirá acceder a métodos private
para la reflexión.
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);
Si el método proporcionado es private
, falla con una NoSuchMethodException
. Podría resolverlo haciendo public
el método o haciendo otra clase para derivarlo.
Para resumir, me preguntaba si había una forma de acceder a un método private
través de la reflexión.
Permítanme proporcionar un código completo para los métodos de ejecución protegida a través de la reflexión. Es compatible con cualquier tipo de parámetros, incluidos genéricos, params autoboxados y valores nulos
@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);
}
Method utiliza apache ClassUtils para verificar la compatibilidad de los params autoboxados
Puede invocar el método privado con la reflexión. Modificando el último bit del código publicado:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
Hay un par de advertencias. Primero, getDeclaredMethod
solo encontrará el método declarado en la Class
actual, no heredado de supertipos. Por lo tanto, recorra la jerarquía de clases concretas si es necesario. En segundo lugar, un SecurityManager
puede evitar el uso del método setAccessible
. Por lo tanto, es posible que deba ejecutarse como una PrivilegedAction
(utilizando AccessController
o Subject
).
Si el método acepta un tipo de datos no primitivo, se puede usar el siguiente método para invocar un método privado de cualquier clase:
public static Object genericInvokMethod(Object obj, String methodName,
int paramCount, Object... params) {
Method method;
Object requiredObj = null;
Object[] parameters = new Object[paramCount];
Class<?>[] classArray = new Class<?>[paramCount];
for (int i = 0; i < paramCount; i++) {
parameters[i] = params[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;
}
Los parámetros aceptados son obj, methodName, el recuento de los parámetros aceptados y los parámetros. Por ejemplo
public class Test {
private String concatString(String a, String b) {
return (a+b);
}
}
El método concatString se puede invocar como
Test t = new Test();
String str = (String) genericInvokMethod(t, "concatString", 2, "Hello", "Mr.x");
Una variante más es el uso de la biblioteca JOOR muy poderosa https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
Permite modificar cualquier campo como las constantes estáticas finales y llamar a los métodos protegidos yne sin especificar una clase concreta en la jerarquía de herencia
<!-- 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>
Use getDeclaredMethod()
para obtener un objeto Method privado y luego use method.setAccessible()
para permitir que realmente lo llame.
puede hacerlo utilizando ReflectionTestUtils of Sring (org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
Ejemplo: si tienes una clase con un método privado "square (int x)"
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);