Любой способ вызвать частный метод?
У меня есть класс, который использует XML и отражение для возврата Object
в другой класс.
Обычно эти объекты являются подполями внешнего объекта, но иногда это то, что я хочу генерировать "на лету". Я пробовал что-то вроде этого, но безрезультатно. Я считаю, что, поскольку Java не позволит вам получить доступ к private
методам для отражения.
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);
Если предоставленный метод private
, он не работает с NoSuchMethodException
. Я мог бы решить это, выполнив метод public
или сделав из него другой класс.
Короче говоря, мне просто интересно, есть ли способ доступа к методу private
через отражение.
Ответы
Ответ 1
Вы можете вызвать частный метод с отражением. Изменение последнего бита опубликованного кода:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
Есть несколько предостережений. Во-первых, getDeclaredMethod
найдет только метод, объявленный в текущем Class
, не унаследованный от супертипов. Итак, перейдите по иерархии конкретного класса, если это необходимо. Во-вторых, a SecurityManager
может предотвратить использование метода setAccessible
. Таким образом, может потребоваться запустить как PrivilegedAction
(используя AccessController
или Subject
).
Ответ 2
Используйте getDeclaredMethod()
, чтобы получить частный объект метода, а затем используйте method.setAccessible()
, чтобы разрешить его на самом деле.
Ответ 3
Если метод принимает непримитивный тип данных, то для вызова закрытого метода любого класса можно использовать следующий метод:
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;
}
Принимаются следующие параметры: obj, methodName и параметры. Например
public class Test {
private String concatString(String a, String b) {
return (a+b);
}
}
Метод concatString может быть вызван как
Test t = new Test();
String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
Ответ 4
Позвольте мне предоставить полный код для выполнения защищенных методов посредством отражения. Он поддерживает любые типы параметров, включая generics, autoboxed params и null values
@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);
}
Метод использует apache ClassUtils для проверки совместимости параметров autoboxed
Ответ 5
Вы можете сделать это, используя ReflectionTestUtils of Spring (org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
Пример: если у вас есть класс с приватным методом square(int x)
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
Ответ 6
Еще один вариант - использование очень мощной библиотеки JOOR https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
Он позволяет изменять любые поля, такие как конечные статические константы, и вызывать защищенные методы без указания конкретного класса в иерархии наследования
<!-- 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>
Ответ 7
Вы можете использовать @JailBreak от Manifold для прямого, типобезопасного отражения Java:
@JailBreak Foo foo = new Foo();
foo.callMe();
public class Foo {
private void callMe();
}
@JailBreak
разблокирует локальную переменную foo
в компиляторе для прямого доступа ко всем членам в иерархии Foo
.
Точно так же вы можете использовать метод расширения jailbreak() для одноразового использования:
foo.jailbreak().callMe();
С помощью метода jailbreak()
вы можете получить доступ к любому члену в иерархии Foo
.
В обоих случаях компилятор разрешает вызов метода для вас безопасно, как если бы это был публичный метод, в то время как Manifold генерирует эффективный код отражения для вас изнутри.
Узнайте больше о коллектор.
Ответ 8
Вы можете использовать @Jailbreak от Manifold для прямого, типобезопасного отражения Java:
@Jailbreak Foo foo = new Foo();
foo.callMe();
public class Foo {
private void callMe();
}
@Jailbreak
разблокирует локальную переменную foo
в компиляторе для прямого доступа ко всем членам в иерархии Foo
.
Точно так же вы можете использовать метод расширения jailbreak() для одноразового использования:
foo.jailbreak().callMe();
С помощью метода jailbreak()
вы можете получить доступ к любому члену в иерархии Foo
.
В обоих случаях компилятор разрешает вызов метода для вас безопасно, как если бы это был публичный метод, в то время как Manifold генерирует эффективный код отражения для вас изнутри.
В качестве альтернативы, если тип статически неизвестен, вы можете использовать Структурную типизацию, чтобы определить интерфейс, который может удовлетворить тип, не объявляя его реализацию. Эта стратегия поддерживает безопасность типов и позволяет избежать проблем производительности и идентичности, связанных с рефлексией и прокси-кодом.
Узнайте больше о коллектор.