簡體   English   中英

關於使用反射的IllegalAccessException

[英]IllegalAccessException on using reflection

我試圖學習反射,我遇到了這個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

我不明白發生了什么。 有任何想法嗎? 提前致謝。

您需要禁止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;
         }
     });
 }
 }

麻煩的代碼是這樣的:

itr.getClass().getMethod

你可能想在Iterator類上使用hasNext 您編寫的是HashMap.KeyIterator類,根據Java語言訪問說明符(或至少是反射使用的JDK 1.0的粗略解釋),您的代碼無法使用它。

改為使用:

Iterator.class.getMethod

(如果不是出於學習目的,請遠離反思。)

您無法訪問它,因為Iterator是一個私有的內部類。 可以在這里找到更多解釋。

很明顯,您當前正在執行的方法無法訪問名為hasNext的方法,例如,它是private還是protected 您可以嘗試使用method.setAccessible(true);啟用對它的訪問method.setAccessible(true);

也可能是您在security manager 定義了一些限制 (如果使用例如linux ,則可能已經包含在發行版java包中的默認值)。

[編輯]事實證明,Tom Hawtin確定了正確的根本原因。 您確實在HashMap.KeyIterator運行。 雖然解決方案是使用Iterator.class而不是itr.getClass()您仍然可以使用setAccessible(true)啟用對它的訪問。

我懷疑你應該使用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(我剛為它添加了支持)。 如果您不使用Maven,請從此處下載jar。 你可以在這里找到問題的測試用例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM