【问题标题】:IllegalAccessException on using reflection使用反射时出现 IllegalAccessException
【发布时间】:2011-07-08 05:50:56
【问题描述】:

我正在尝试学习反射,但遇到了这个 IllegalAccessException。请看以下代码:

public class ReflectionTest
{
      public static void main(String[] args)
      {
           Set<String> myStr = new HashSet<String>();
           myStr.add("obj1");
           Iterator itr = myStr.iterator();
           Method mtd = itr.getClass().getMethod("hasNext");
           System.out.println(m.invoke(it));
      }
} 

当我尝试运行这个程序时,我得到了以下信息:

Exception in thread "main" IllegalAccessException

我不明白发生了什么。有任何想法吗?提前致谢。

【问题讨论】:

  • 应该System.out.println(m.invoke(it));System.out.println(mtd.invoke(itr)); 吗?您似乎正在尝试访问私人成员。

标签: java reflection


【解决方案1】:

您需要禁止 Java 语言访问检查,以便反射性地调用另一个类中的私有方法,使用 setAccessible(true):

Method mtd= itr.getClass().getMethod("hasNext");
  if(!mtd.isAccessible()) {
      mtd.setAccessible(true);
 }

此外,当启用 SecurityManager 时,我们需要额外的权限来调用 setAccessible(true)。否则,我们得到:

C:\ReflectionTest>java -Djava.security.manager CallFoo
Exception in thread "main" java.security.AccessControlException: access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:264)
    at java.security.AccessController.checkPermission(AccessController.java:427)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
    at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:107)
    at CallFoo.main(CallFoo.java:8)

我们只想将此 suppressAccessChecks 权限授予受信任的代码源,绝对不会授予调用堆栈中的所有类。所以我们要修改CallFoo.java:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

public class CallFoo {
  public static void main(String args[]) throws Exception {
     doCallFoo();
  }

 public static void doCallFoo() throws IllegalAccessException, ClassNotFoundException, NoSuchMethodException, 
         InvocationTargetException, InstantiationException, PrivilegedActionException {
       Class fooClass = Class.forName("Foo");
     final Foo foo = (Foo) fooClass.newInstance();
     final Method helloMethod = fooClass.getDeclaredMethod("hello");

     AccessController.doPrivileged(new PrivilegedExceptionAction() {
         public Object run() throws Exception {
             if(!helloMethod.isAccessible()) {
                 helloMethod.setAccessible(true);
             }
             helloMethod.invoke(foo);
           return null;
         }
     });
 }
 }

【讨论】:

  • 你刚刚复制粘贴了blogs.oracle.com/chengfang/entry/…吗?
  • 有趣!不过我不记得了。
  • -1:引用你的来源。这是抄袭,不管你是否记得。更重要的是,这并没有回答问题—— Iterator.hasNext() 不是私有的。
  • 我想建议这个new PrivilegedExceptionAction&lt;Void&gt;() {,关于日食警告
【解决方案2】:

麻烦的一段代码是这样的:

itr.getClass().getMethod

您可能希望在 Iterator 类中使用 hasNext。您所写的是HashMap.KeyIterator 类,根据Java 语言访问说明符(或至少反射使用的JDK 1.0 的粗略解释),您的代码无法使用该类。

改用:

Iterator.class.getMethod

(如果不是出于学习目的,请远离反思。)

【讨论】:

【解决方案3】:

您无法访问它,因为 Iterator 是私有内部类。更多解释可以看here

【讨论】:

    【解决方案4】:

    很明显,您当前执行的方法无法访问名为 hasNext 的方法,例如,它是 privateprotected。您可以尝试使用method.setAccessible(true); 启用对它的访问权限

    也可能是您的security manager 中有一些restrictions defined(如果您使用例如linux可能 已包含在发行版java 包中的默认值)。

    [编辑] 事实证明,Tom Hawtin 确定了正确的根本原因。您确实在HashMap.KeyIterator 上运行。虽然解决方案是使用Iterator.class 而不是itr.getClass(),但您仍然可以使用setAccessible(true) 启用对它的访问。

    【讨论】:

    • hasNext 不是私人的。你的第二个陈述可能是真的。
    • @adarshr,结果是hasNext has private 访问您的代码操作的HashMap.KeyIterator 类型的迭代器。
    【解决方案5】:

    我怀疑您应该使用 getDeclaredMethod(以及其他问题)。我不想记住反射 API 的详细信息(它们是用于编译器的!),但在您的情况下,将您的代码与 dp4j 生成的代码进行比较:

    $ javac -Averbose=true -All -cp dp4j-1.2-SNAPSHOT-jar-with-dependencies.jar ReflectionTest.java 
    ReflectionTest.java:6: Note: 
    import java.util.*;
    
    public class ReflectionTest {
    
    public ReflectionTest() {
        super();
    }
    
    @com.dp4j.Reflect()
    public static void main(String[] args) throws java.lang.ClassNotFoundException, java.lang.NoSuchFieldException, java.lang.IllegalAccessException, java.lang.NoSuchMethodException, java.lang.reflect.InvocationTargetException, java.lang.InstantiationException, java.lang.IllegalArgumentException {
        final java.lang.reflect.Constructor hashSetConstructor = Class.forName("java.util.HashSet").getDeclaredConstructor();
        hashSetConstructor.setAccessible(true);
        Set<String> myStr = (.java.util.Set<.java.lang.String>)hashSetConstructor.newInstance();
        final java.lang.reflect.Method addWithEMethod = Class.forName("java.util.Set").getDeclaredMethod("add", .java.lang.Object.class);
        addWithEMethod.setAccessible(true);
        addWithEMethod.invoke(myStr, new .java.lang.Object[1][]{"obj1"});
        final java.lang.reflect.Method iteratorMethod = Class.forName("java.util.Set").getDeclaredMethod("iterator");
        iteratorMethod.setAccessible(true);
        Iterator itr = (.java.util.Iterator)iteratorMethod.invoke(myStr);
        final java.lang.reflect.Method hasNextMethod = Class.forName("java.util.Iterator").getDeclaredMethod("hasNext");
        hasNextMethod.setAccessible(true);
        final java.lang.reflect.Method printlnWithbooleanMethod = Class.forName("java.io.PrintStream").getDeclaredMethod("println", .java.lang.Boolean.TYPE);
        printlnWithbooleanMethod.setAccessible(true);
        printlnWithbooleanMethod.invoke(System.out, new .java.lang.Object[1][]{hasNextMethod.invoke(itr)});
    }
    

    }

        public static void main(String[] args)
                           ^
    ...
    
    $ java ReflectionTest
    true
    

    您需要做的唯一更改是使用 @com.dp4j.Reflect 注释您的 main 方法:

    $ vim ReflectionTest.java
    import java.util.*;
    
    public class ReflectionTest
    {
            @com.dp4j.Reflect
            public static void main(String[] args)
            {
                    Set<String> myStr = new HashSet<String>();
                    myStr.add("obj1");
                    Iterator itr = myStr.iterator();
                    // Method mtd = itr.getClass().getMethod("hasNext");
                    System.out.println(itr.hasNext());
            }
    }
    

    注意:这仅适用于 dp4j-1.2-SNAPSHOT(我刚刚为它添加了支持)。如果您不使用 Maven,请从 here 下载 jar。你找到了你的问题的测试用例here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-09
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多