有啥方法可以调用私有方法?

Posted

技术标签:

【中文标题】有啥方法可以调用私有方法?【英文标题】:Any way to Invoke a private method?有什么方法可以调用私有方法? 【发布时间】:2010-10-27 04:37:49 【问题描述】:

我有一个类使用 XML 和反射将 Objects 返回到另一个类。

通常这些对象是外部对象的子字段,但有时我想即时生成。我尝试过这样的事情,但无济于事。我相信这是因为 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】:

您可以使用 Manifold's @Jailbreak 进行直接、类型安全的 Java 反射:

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo 
    private void callMe();

@Jailbreak 在编译器中解锁foo 局部变量,以便直接访问Foo 层次结构中的所有成员。

同样你可以使用 jailbreak() 扩展方法一次性使用:

foo.jailbreak().callMe();

通过jailbreak() 方法,您可以访问Foo 层次结构中的任何成员。

在这两种情况下,编译器都会像公共方法一样安全地为您解析方法调用,而 Manifold 会在后台为您生成高效的反射代码。

或者,如果类型不是静态已知的,您可以使用Structural Typing 定义一个类型可以满足的接口,而无需声明其实现。此策略维护类型安全并避免与反射和代理代码相关的性能和身份问题。

了解更多关于Manifold的信息。

【讨论】:

【参考方案2】:

如果方法接受非原始数据类型,则可以使用以下方法调用任何类的私有方法:

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");

【讨论】:

为什么需要 paramCount?你不能只使用 params.length 吗?【参考方案3】:

你可以使用 Spring 的 ReflectionTestUtils (org.springframework.test.util.ReflectionTestUtils) 来做到这一点

ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);

示例:如果您有一个带有私有方法的类square(int x)

Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);

【讨论】:

核心Spring中还有一个更有限的ReflectionUtils。【参考方案4】:

另一个变种是使用非常强大的 JOOR 库 https://github.com/jOOQ/jOOR

MyObject myObject = new MyObject()
on(myObject).get("privateField");  

它允许修改最终静态常量等任何字段并调用 yne 受保护的方法,而无需在继承层次结构中指定具体类

<!-- 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>

【讨论】:

【参考方案5】:

让我提供通过反射执行受保护方法的完整代码。它支持任何类型的参数,包括泛型、自动装箱参数和空值

@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 来检查自动装箱参数的兼容性

【讨论】:

回答一个已有 9 年历史、浏览量超过 90000 且已被接受的答案绝对没有意义。改为回答未回答的问题。【参考方案6】:

您可以通过反射调用私有方法。修改贴出代码的最后一位:

Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

有几个注意事项。首先,getDeclaredMethod 只会找到在当前Class 中声明的方法,而不是从超类型继承的。因此,如有必要,向上遍历具体的类层次结构。其次,SecurityManager 可以阻止使用setAccessible 方法。因此,它可能需要以PrivilegedAction 运行(使用AccessControllerSubject)。

【讨论】:

过去我做这个的时候,我也调用过method.setAccessible(false),但是不知道有没有必要。 不,当您设置可访问性时,它仅适用于该实例。只要你不让那个特定的 Method 对象脱离你的控制,它就是安全的。 那么,如果私有方法可以从类外部调用,那又有什么意义呢? 另外,请确保您调用 getDeclaredMethod() 而不仅仅是 getMethod() - 这不适用于私有方法。 @PeterAjtai 很抱歉回复晚了,但请这样想:现在大多数人都会锁上门,即使他们知道锁可以被轻易打破或完全绕过。为什么?因为它有助于让大多数诚实的人保持诚实。你可以认为private access 扮演着类似的角色。【参考方案7】:

使用getDeclaredMethod() 获取私有 Method 对象,然后使用method.setAccessible() 允许实际调用它。

【讨论】:

在我自己的例子中 (***.com/a/15612040/257233) 如果我不调用 setAccessible(true),我会得到一个 java.lang.***Error

以上是关于有啥方法可以调用私有方法?的主要内容,如果未能解决你的问题,请参考以下文章

java反射机制可以调用到私有方法,是否就破坏了JAVA的卦装性呢。

Java中私有构造函数有啥用?

有啥方法可以进行同步 PageMethods 调用?

有啥方法可以将函数调用为字符串?

有啥方法可以强制异步 NSURLConnection 在后台调用 connectionDidFinishLoading 吗?

调用私有方法保留调用栈