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

lymoge 2014-04-25 01:44:47

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和反射,遇到此问题,望高手指教。
...全文
438 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
草原的雨夜 2014-04-28
  • 打赏
  • 举报
回复
引用 5 楼 lymoge 的回复:
[quote=引用 1 楼 yangweihong20110929 的回复:] 自定义类加载的机制中反射获取类对象时会有安全机制的限制,所以只能用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);
	}
}

[/quote] 对头,就是反射的限制,加载器没限制,加载还是可以正常加载的,但是获取实例的时候有私有成员安全验证。所以问题不在加载而在反射。。。
草原的雨夜 2014-04-25
  • 打赏
  • 举报
回复
自定义类加载的机制中反射获取类对象时会有安全机制的限制,所以只能用public。如果想深入研究应该多读点jdk源码,这样子可能了解的多了,就更容易理解了。
lymoge 2014-04-25
  • 打赏
  • 举报
回复
引用 1 楼 yangweihong20110929 的回复:
自定义类加载的机制中反射获取类对象时会有安全机制的限制,所以只能用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);
	}
}

lymoge 2014-04-25
  • 打赏
  • 举报
回复
引用 2 楼 fangmingshijie 的回复:

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

你的意思我明白,但是异常不是这里抛出的。是sun.reflect.Reflection.ensureMemberAccess抛出的。

        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
草原的雨夜 2014-04-25
  • 打赏
  • 举报
回复
引用 2 楼 fangmingshijie 的回复:

    public T newInstance() 
        throws InstantiationException, IllegalAccessException
    {
	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();
    }

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧