可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I was trying to learn reflection and I came across this IllegalAccessException. Please see the following code:
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));
}
}
When I tried to run this program, I got the following:
Exception in thread "main" IllegalAccessException
I don't understand what's going on. Any ideas? Thanks in advance.
回答1:
You need to suppress Java language access checking in order to reflectively invoke a private method in another class, with setAccessible(true):
Method mtd= itr.getClass().getMethod("hasNext");
if(!mtd.isAccessible()) {
mtd.setAccessible(true);
}
Furthermore, when a SecurityManager is enable, we need extra permissions to call setAccessible(true). Otherwise, we get:
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)
We only want to grant this suppressAccessChecks permission to trusted code source, definitely not to all classes in the call stack. So we would modify 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;
}
});
}
}
回答2:
The troublesome piece of code is this:
itr.getClass().getMethod
You probably wanted hasNext
on the Iterator
class. What you have written is the HashMap.KeyIterator
class, which according the Java language access specifiers (or at least the rough interpretation of JDK 1.0 used by reflection) is not available to your code.
Use instead:
Iterator.class.getMethod
(And if it wasn't for learning purposes, stay away from reflection.)
回答3:
You cannot access it, because the Iterator is a private inner class. More explanation can be found here.
回答4:
It's apparent that your currently executing method does not have access to the method named hasNext
, e.g., by it being private
or protected
. You could try to enable access to it using method.setAccessible(true);
It might also be that you have some restrictions defined in your security manager
(which, if you use e.g., linux
, might have been included default from the distributions java package).
[EDIT] As it turns out, Tom Hawtin identified the correct root cause. You are indeed operating on HashMap.KeyIterator
. Although the solution would be to use Iterator.class
instead of itr.getClass()
you could still enable access to it using setAccessible(true)
.
回答5:
I suspect you should use getDeclaredMethod (among other issues). I don't bother to remember Reflection API details (they're for the compiler!), but in your case compare your code with that produced by 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
The only change you need to do is annotate your main method with @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());
}
}
NB: this works only with dp4j-1.2-SNAPSHOT (I've just added suport for it). If you don't use Maven, download the jar from here. You find the test case with your problem here.