日期:2014-05-20  浏览次数:20788 次

自定义的ClassLoader加载非public类创建的Class对象不能创建实例?

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class Test {

public class MyLoader extends ClassLoader {
private final String path = getClass().getResource("/").getPath();

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = getData(name);
return defineClass(name, b, 0, b.length);
}

private byte[] getData(String url) {
byte[] b = null;
try (FileInputStream reader = new FileInputStream(new StringBuffer(
path).append(url.replace('.', File.separatorChar))
.append(".class").toString())) {
b = new byte[reader.available()];
reader.read(b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
}
return b;
}
}

public static void main(String[] args) throws Exception {
new Test().foo();
}

private void foo() throws Exception {
Class<?> cls = Foo.class;

/* 使用MyLoader加载非public类 */
// cls = new MyLoader().findClass(Foo.class.getName());

Object obj = cls.newInstance();// java.lang.IllegalAccessException异常
cls.getDeclaredMethod("bar").invoke(obj);
}
}




/*public*/ class Foo {
public void bar() {
System.out.println("asdfghjkl");
}
}



刚了解ClassLoader和反射,遇到此问题,望高手指教。
------解决方案--------------------
自定义类加载的机制中反射获取类对象时会有安全机制的限制,所以只能用public。如果想深入研究应该多读点jdk源码,这样子可能了解的多了,就更容易理解了。
------解决方案--------------------

    public T newInstance() 
        throws InstantiationExceptionIllegalAccessException
    {
if (System.getSecurityManager() != null) {
    checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
    }


------解决方案--------------------
引用:

    public T newInstance() 
        throws InstantiationException, IllegalAccessException
    {
if (System.getSecurityManager() != null) {
    checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
    }


还是版主威武,直接上源码,呵呵,省得解释那么多了。。。
------解决方案--------------------
引用:
Quote: 引用:

自定义类加载的机制中反射获取类对象时会有安全机制的限制,所以只能用public。如果想深入研究应该多读点jdk源码,这样子可能了解的多了,就更容易理解了。

是这么回事,但也并不是只能用public,可能是被帖子标题误导了。我分析了下源码,因为newInstance要调用构造函数(如果类为public,但构造非public也是如此),但是默认是不可访问的。所以用反射来实例化,并设置为Accessible,就可以了,所以这只是反射的限制,应该不是类加载器的限制。


private void foo() throws Exception {
Class<?> cls = Foo.class;

/* non-public Foo */
cls = new MyLoader().findClass(Foo.class.getName());

// Object obj = cls.newInstance(); // java.lang.IllegalAccessException
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();

Method method = cls.getDeclaredMethod("bar");
method.setAccessible(true);
method.invoke(obj);
}
}



对头,就是反射的限制,加载器没限制,加载还是可以正常加载的,但是获取实例的时候有私有成员安全验证。所以问题不在加载而在反射。。。