Есть какой-нибудь способ вызвать частный метод?
-
22-08-2019 - |
Вопрос
У меня есть класс, который использует 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
метод через отражение.
Решение
Вы можете вызвать частный метод с помощью отражения.Изменение последнего бита опубликованного кода:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
Есть пара предостережений.Первый, getDeclaredMethod
найдет только метод, объявленный в текущем Class
, не наследуется от супертипов.Итак, при необходимости пройдите вверх по иерархии конкретных классов.Во-вторых, a SecurityManager
может предотвратить использование setAccessible
способ.Таким образом, возможно, потребуется запустить как PrivilegedAction
(используя AccessController
или Subject
).
Другие советы
Использование getDeclaredMethod()
чтобы получить объект частного метода, а затем использовать method.setAccessible()
чтобы позволить на самом деле вызвать это.
Если метод принимает непримитивный тип данных, то следующий метод может быть использован для вызова частного метода любого класса:
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");
вы можете сделать это, используя 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);
Позвольте мне предоставить полный код для выполнения защищенных методов с помощью отражения.Он поддерживает любые типы параметров, включая общие, автоматически загружаемые параметры и нулевые значения
@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 для проверки совместимости автоматически заблокированных параметров
Еще один вариант - использование очень мощной библиотеки 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>
Вы можете использовать Коллекторные @Побег из тюрьмы для прямого, типобезопасного отражения Java:
@Jailbreak Foo foo = new Foo();
foo.callMe();
public class Foo {
private void callMe();
}
@Jailbreak
разблокирует foo
локальная переменная в компиляторе для прямого доступа ко всем элементам в Foo
иерархия.
Аналогичным образом вы можете использовать побег из тюрьмы() метод расширения для одноразового использования:
foo.jailbreak().callMe();
Через jailbreak()
метод, которым вы можете получить доступ к любому участнику в Foo
иерархия.
В обоих случаях компилятор разрешает вызов метода для вас типобезопасно, как для общедоступного метода, в то время как Manifold генерирует эффективный код отражения для вас под капотом.
В качестве альтернативы, если тип неизвестен статически, вы можете использовать Структурная типизация чтобы определить интерфейс, которому может удовлетворять тип, не нужно объявлять его реализацию.Эта стратегия обеспечивает безопасность типов и позволяет избежать проблем с производительностью и идентификацией, связанных с отражением и прокси-кодом.
Узнайте больше о Многообразие.