您的位置:首页 > 编程语言 > Java开发

java类加载器

2016-06-04 16:12 441 查看
详细内容请参考原链接:http://blog.csdn.net/jiangwei0910410003/article/details/17733153,此文只是转载

1、新建一个java对象,JVM需要把这个对象对应的字节码加载到内存中,字节码就是对应的Class文件,自己java工程,其在bin目录下,也就是把.class文件的内容读到内存中;

2、Java虚拟机中类加载器

java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++写的,内置在JVM内核中,也就是所有类的鼻祖;

之所以有不同的类加载器,是因为不同的类加载器加载不同目录下的.class文件;采用委托的方式,向父类请求加载,每个父类去指定的目录加载,成功结束;不成功再顺序往下请求,直到加载成功,所有加载类都没有成功,则抛出ClassNotFoundException异常

类加载器的委托机制:

当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?

(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)

(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B

(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类

每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束;

因为BootStrap不是java类,所以打印出来的class是null

3、System类、list、map这样的类都在rt.jar中,需要由BootStrap来加载,所以其加载类打印出来为null;也就是说,谁加载的你当前这个jar,那么它的classloader就是谁,比方说把MyClassLoader打包成jar,然后放到jre1.x/lib/ext目录下,那么它的第一个加载类就会变成sun.misc.Launcher$ExtClassLoader

4、可以定义自己的类加载器,需要将自己的类加载器挂载到系统类加载器树上,在构造函数时可以指定类加载器

5、定义了自己的类加载器,每个加载器都有自己的搜索目录,自己类加载器有自己的目录,而它的父类也有自己的目录,所以类名字,如果父类找到了,那么子类不会继续查找了;所以即使使用了自己的类加载器,如果是父类找到了对应名称的类class,其加载器就是父类

编写自己的加载类:

先写一个测试类:

public class ClassLoaderAttachment extends Date{
private static final long serialVersionUID = 98398492342L;
@Override
public String toString() {
// TODO Auto-generated method stub

return "Hell ClassLoaderAttachment";
}
}


编译后,在bin目录会生成它的.class文件,然后在项目目录(bin的上一层把)创建一个”temp_folder”目录,用于生成加密后的.class文件

写一个main函数,把上面的ClassLoaderAttachment.class文件加密

//测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下
public static void main(String[] args) throws Exception{
//配置运行参数
String srcPath = args[0];//ClassLoaderAttachment.class原路径
String desPath = args[1];//ClassLoaderAttachment.class输出的路径
String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String desPathFile = desPath + "/" + desFileName;
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(desPathFile);
//将class进行加密
encodeAndDecode(fis,fos);
fis.close();
fos.close();
}

/**
* 加密和解密算法
* @param is
* @param os
* @throws Exception
*/
private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{
int bytes = -1;
while((bytes = is.read())!= -1){
bytes = bytes ^ 0xff;//和0xff进行异或处理
os.write(bytes);
}
}


会在temp_folder目录下生成一个新的ClassLoaderAttachment.class,此目录和class文件名用于创建自己的ClassLoader时,作为参数传入

下面编写自己的加载类

public class myLoadClass extends ClassLoader{

//需要加载类.class文件的目录
private String classDir;

//无参的构造方法,用于class.newInstance()构造对象使用
public myLoadClass(){
}

public myLoadClass(String classDir){
this.classDir = classDir;
}

@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//class文件的路径
String classPathFile = classDir + "/" + name + ".class";
try {
//将class文件进行解密
FileInputStream fis = new FileInputStream(classPathFile);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
encodeAndDecode(fis,bos);
byte[] classByte = bos.toByteArray();
//将字节流变成一个class
return defineClass(classByte,0,classByte.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}

//测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下
public static void main(String[] args) throws Exception{
//配置运行参数
String srcPath = args[0];//ClassLoaderAttachment.class原路径
String desPath = args[1];//ClassLoaderAttachment.class输出的路径
String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String desPathFile = desPath + "/" + desFileName;
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(desPathFile);
//将class进行加密
encodeAndDecode(fis,fos);
fis.close();
fos.close();
}

/**
* 加密和解密算法
* @param is
* @param os
* @throws Exception
*/
private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{
int bytes = -1;
while((bytes = is.read())!= -1){
bytes = bytes ^ 0xff;//和0xff进行异或处理
os.write(bytes);
}
}
}


其中的main函数就是刚才加密class文件用的

然后写测试myDexLoader的测试方法:

public class myMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//输出ClassLoaderText的类加载器名称
System.out.println("ClassLoaderText类的加载器的名称:"+myMain.class.getClassLoader().getClass().getName());
System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
System.out.println("List类的加载器的名称:"+List.class.getClassLoader());

ClassLoader cl = myMain.class.getClassLoader();
while(cl != null){
System.out.print(cl.getClass().getName()+"->");
cl = cl.getParent();
}
System.out.println(cl);
/**
* 如果在bin目录下有ClassLoaderAttachment.class,那么用的是系统的appClassLader,因为父类已经找到了
* 如果想自己的类加载,那么需要把bin目录下对应的.class文件删掉,使父类找不到,这样自己的加载类才有机会去执行
* **/
Class myClass = new myLoadClass("temp_folder").loadClass("ClassLoaderAttachment");
Date myDate = (Date)myClass.newInstance();
System.out.println("classLoader is :"+myDate.getClass().getClassLoader().getClass().getName());
System.out.println("date result is "+myDate.toString());
ClassLoader cll = myDate.getClass().getClassLoader();
while(cll != null){
System.out.print(cll.getClass().getName()+"->");
cll = cll.getParent();
}
System.out.println(cll);
}
}


也是一个main方法,执行即可;把bin目录下的ClassLoaderAttachment.class删除后,最后得到的加载类链表是:



由于temp_folder目录下是加了密的class文件,所以只能被我们自己能解密的ClassLoader进行加载执行,其实就是返回真是的字节码;

如果把它拷贝到bin目录,那么系统的sun.misc.Launcher$AppClassLoader会进行加载此class,但是文件是加了密的,所以其解析错误,则会报执行错误
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java