论坛首页 Java企业应用论坛

类加载器与name-space

浏览 6569 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-01-21  
学习自定义类加载器与运行时包:
package test;

import java.io.InputStream;

public class NewClassLoader extends ClassLoader {
	public MySingleton createNewOne() throws Exception {
		InputStream is = getClass().getResourceAsStream("MySingletonImp.class");
		byte[] b = new byte[is.available()];
		is.read(b);
		Class clz = defineClass(null, b, 0, b.length);
		Object o = clz.newInstance();
		return (MySingleton) o;
	}

	public static void main(String[] args) throws Exception {
		NewClassLoader loader = new NewClassLoader();
		MySingleton newObj = loader.createNewOne();
		MySingletonImp instance = MySingletonImp.getInstance();
		System.out.println(MySingletonImp.class.getClassLoader());
		System.out.println(instance == newObj);
	}
}

class MySingletonImp implements MySingleton {
	private static final MySingletonImp instance = new MySingletonImp();

	public static MySingletonImp getInstance() {
		return instance;
	}
}

以上两个类是放在同一个文件下面
package test;

public interface MySingleton {

}

接口MySingleton 单独放在一个文件下面,此时执行mian方法,则抛异常:
Exception in thread "main" java.lang.IllegalAccessException: Class test.NewClassLoader can not access a member of class test.MySingletonImp with modifiers ""如果把 MySingletonImp 类提出来单独放一个文件
package test;

public class MySingletonImp implements MySingleton {
	private static final MySingletonImp instance = new MySingletonImp();

	public static MySingletonImp getInstance() {
		return instance;
	}
}

则执行不会抛异常,输出 false
baidu描述原因:
Java语言中的包访问成员(friendly)实际上指的是运行时包访问可见,而不是编译时。因此当你试图访问不在同一个runtime package的成员是(即便在编译时它们在同一个包内,但是却由不同的class loader加载)也同样会得到java.lang.IllegalAccessException: Class A can not access a member of class B with modifiers "" 这样的异常。

但是本人还是不是很理解,我把MySingletonImp 单独提出来时,也不是同一个 runtime package,因为加载器都不一样,NewClassLoader 是系统类加载器加载的,而MySingletonImp 是自定义加载器加载的。。很是不明白,
欢迎大家讨论...
   发表时间:2010-01-21   最后修改:2010-01-21
前后两次的差异源于你在把MySingletonImp提取到一个单独的文件后,把它的可访问性也改变了:原本是package,后来是public。

如你所知,只有当两个类是被同一个classloader加载,并且它们的包名相同时,它们所在的的“运行时包”才是相同的;拥有package可访问性的类从别的包无法直接访问。
在调用Class.newInstance()时,里面会对可访问性做检查。当它发现被实例化的类的可访问性为public,并且其默认构造器的可访问性也是public时,检查就算通过了;如果类的可访问性不是public(对于顶层类来说这意味这它是package可见),则要检查发起实例化的类与被实例化的类是否位于同一个“运行时包”内,如果不是则校验失败,抛出IllegalAccessException。

再回头看顶楼的例子,在修改代码前,test.MySingletonImp类的可访问性是package,其默认构造器没有写,由编译器自动生成public的默认构造器。加载NewClassLoader类的类加载器是当前线程的上下文类加载器。然后在NewClassLoader.createNewOne()中,NewClassLoader自行定义了一个test. MySingletonImp;被定义的MySingletonImp与NewClassLoader就不在同一个“运行时包”里了,因而未能通过可访问性检查。
在修改代码后,test. MySingletonImp的可访问性变为public,后面的一系列检查都不需要做了,因而没出异常。
0 请登录后投票
   发表时间:2010-01-21   最后修改:2010-01-21
推荐你这个开源工具
http://classworlds.codehaus.org/
0 请登录后投票
   发表时间:2010-01-21  
RednaxelaFX 写道
前后两次的差异源于你在把MySingletonImp提取到一个单独的文件后,把它的可访问性也改变了:原本是package,后来是public。

如你所知,只有当两个类是被同一个classloader加载,并且它们的包名相同时,它们所在的的“运行时包”才是相同的;拥有package可访问性的类从别的包无法直接访问。
在调用Class.newInstance()时,里面会对可访问性做检查。当它发现被实例化的类的可访问性为public,并且其默认构造器的可访问性也是public时,检查就算通过了;如果类的可访问性不是public(对于顶层类来说这意味这它是package可见),则要检查发起实例化的类与被实例化的类是否位于同一个“运行时包”内,如果不是则校验失败,抛出IllegalAccessException。

再回头看顶楼的例子,在修改代码前,test.MySingletonImp类的可访问性是package,其默认构造器没有写,由编译器自动生成public的默认构造器。加载NewClassLoader类的类加载器是当前线程的上下文类加载器。然后在NewClassLoader.createNewOne()中,NewClassLoader自定定义了一个test. MySingletonImp;被定义的MySingletonImp与NewClassLoader就不在同一个“运行时包”里了,因而未能通过可访问性检查。
在修改代码后,test. MySingletonImp的可访问性变为public,后面的一系列检查都不需要做了,因而没出一场。

这位兄台讲的够清楚了。。。赞一个,研究的够深!
0 请登录后投票
   发表时间:2010-01-22  
更改代码这部分代码
public MySingleton createNewOne() throws Exception {
		InputStream is = getClass().getResourceAsStream("MySingletonImp.class");
		byte[] b = new byte[is.available()];
		is.read(b);
		Class clz = defineClass(null, b, 0, b.length);
		Object o = clz.newInstance();
		return (MySingleton) o;
	}


public MySingletonImp createNewOne() throws Exception {
		InputStream is = getClass().getResourceAsStream("MySingletonImp.class");
		byte[] b = new byte[is.available()];
		is.read(b);
		Class clz = defineClass(null, b, 0, b.length);
		Object o = clz.newInstance();
		return (MySingletonImp) o;
	}

并更改
MySingletonImp newObj = loader.createNewOne();

执行后,会产生类型转换异常,这是由于 NewClassLoader 是系统类加载器加载的,而MySingletonImp 是 自定义加载器NewClassLoader 加载,而这两个加载器不存在父子关系,即不属于相同的namespace ,进而不可见。
子加载器包含所有父加载器的namespace,即子加载器加载类可以访问所有父加载器加载的类,反之则不然。
0 请登录后投票
   发表时间:2010-01-22  
推荐大家看一看加入我们的100937543,大家可以进行研究和商讨。
0 请登录后投票
   发表时间:2010-01-22  
请问 为什么要用自定义加载器呢 我是小菜鸟
0 请登录后投票
   发表时间:2010-01-22  
pengjunwu 写道
请问 为什么要用自定义加载器呢 我是小菜鸟

可以解决类似的问题:
比如:我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的任何任务。当任务被提交到这个引擎,首先需要加载这个任务的代码。假设不同的客户对此引擎提交了不同的任务,凑巧,这些所有的任务都有一个相同的类名和包名。现在面临的问题就是这个引擎是否可以针对不同的用户所提交的信息而做出不同的反应
而使用了自定义加载器后,我们可以针对每个客户提交任务实例化一个加载器来加载客户提交的任务类,由于运行时包由 类加载器和包名决定,所以即使是客户都提交相同的任务类,在同一个jvm中,不同的运行包的类是不可见,所以jvm可以处理好即使每个客户提交一个相同的类名和包名。。
具体见下周贴代码示意..顺便我也是正在学习的小菜。。
0 请登录后投票
   发表时间:2010-01-23  
xuyan2680 写道
pengjunwu 写道
请问 为什么要用自定义加载器呢 我是小菜鸟

可以解决类似的问题:
比如:我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的任何任务。当任务被提交到这个引擎,首先需要加载这个任务的代码。假设不同的客户对此引擎提交了不同的任务,凑巧,这些所有的任务都有一个相同的类名和包名。现在面临的问题就是这个引擎是否可以针对不同的用户所提交的信息而做出不同的反应
而使用了自定义加载器后,我们可以针对每个客户提交任务实例化一个加载器来加载客户提交的任务类,由于运行时包由 类加载器和包名决定,所以即使是客户都提交相同的任务类,在同一个jvm中,不同的运行包的类是不可见,所以jvm可以处理好即使每个客户提交一个相同的类名和包名。。
具体见下周贴代码示意..顺便我也是正在学习的小菜。。


这样是不是设计上有点问题啊?对用户上传的业务类是不是应该有个命名规则!
0 请登录后投票
   发表时间:2010-01-24  
inside jvm里面有讲
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics