IllegalAccessException при использовании отражения - PullRequest
14 голосов
/ 03 марта 2011

Я пытался научиться размышлять, и я столкнулся с этим 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

Я не понимаю, что происходит. Есть идеи? Заранее спасибо.

Ответы [ 5 ]

32 голосов
/ 03 марта 2011

Вам необходимо подавить проверку доступа к языку 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;
         }
     });
 }
 }
8 голосов
/ 03 марта 2011

Неприятный фрагмент кода таков:

itr.getClass().getMethod

Возможно, вы хотели hasNext в классе Iterator.Вы написали класс HashMap.KeyIterator, который согласно спецификаторам доступа к языку Java (или, по крайней мере, грубой интерпретации JDK 1.0, используемой для отражения) не доступен для вашего кода.

Вместо этого используйте:

Iterator.class.getMethod

(И если это было не в учебных целях, держитесь подальше от размышлений.)

2 голосов
/ 03 марта 2011

Вы не можете получить к нему доступ, потому что Iterator является закрытым внутренним классом.Более подробное объяснение можно найти здесь .

0 голосов
/ 04 марта 2011

Я подозреваю, что вы должны использовать getDeclaredMethod (среди других вопросов). Я не беспокоюсь о деталях Reflection 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:

$ 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 (я только что добавил для этого suport). Если вы не используете Maven, скачайте банку с здесь . Вы найдете тестовый пример с вашей проблемой здесь .

0 голосов
/ 03 марта 2011

Очевидно, что ваш текущий выполняемый метод не имеет доступа к методу с именем hasNext, например, из-за того, что он private или protected.Вы можете попытаться разрешить доступ к нему, используя method.setAccessible(true);

. Возможно, у вас есть ограничения, определенные в вашем security manager (которые, если вы используете, например, linux может было включено по умолчанию из пакета java дистрибутивов).

[РЕДАКТИРОВАТЬ] Оказывается, Том Хотин определил правильную основную причину.Вы действительно работаете на HashMap.KeyIterator.Хотя решением было бы использовать Iterator.class вместо itr.getClass(), вы все равно могли бы разрешить доступ к нему, используя setAccessible(true).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...