题
我有一个使用 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
声明的方法,而不是从父类型继承。所以,如果有必要遍历了具体的类层次结构。其次,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,方法名和参数。例如
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");
你可以使用 Spring 的 ReflectionTestUtils 来做到这一点(org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
例子 :如果你有一个带有私有方法的类 square(int x)
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
让我要通过反射执行保护方法提供完整代码。它支持任何类型的PARAMS包括泛型,autoboxed PARAMS和空值的
@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则params的兼容性
一个更变体是使用非常强大JOOR库 https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
它允许修改等最终静态常量的任何字段,并调用炔保护方法,而没有在继承hierarhy指定具体的类
<!-- 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>
您可以使用歧管的 @Jailbreak 作为直接,类型-safe Java反射:
@Jailbreak Foo foo = new Foo();
foo.callMe();
public class Foo {
private void callMe();
}
@Jailbreak
解锁在编译器为在foo
层次结构直接访问所有的成员Foo
局部变量。
类似地,可以使用越狱()强>扩展方法用于一次性使用:
foo.jailbreak().callMe();
通过jailbreak()
方法可以访问任何构件在Foo
的层次结构。
在这两种情况下,编译器为键入-安全,因为如果一个公共方法,而歧管罩下为您生成高效反射代码解析方法调用。
可替换地,如果该类型不是静态已知的,则可以使用结构打字定义的接口的类型能满足无需声明其执行。这一战略保持类型安全,并避免了反射和代理代码相关的性能和身份的问题。
了解更多有关集成块。