xscn

博客园 首页 新随笔 联系 订阅 管理

 

类加载器

用来加载Java类到Java虚拟机中。一般来说,Java虚拟机使用Java类的方式如下:Java 源程序(.java 文件)在经过Java编译器编译之后就被转换成字节码(.class 文件)。类加载器就是负责读取Java字节代码,并转换成 java.lang.Class类的一个实例。

Java应用环境中不同的class分别由不同的ClassLoader负责加载。
一个jvm中默认的类加载器有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader:

  • 引导类加载器Bootstrap ClassLoader     负责加载Java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等。
  • 扩展类加载器Extension ClassLoader     负责加载Java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class。
  • 应用类加载器App ClassLoader             负责加载当前Java应用的classpath中的所有类。 

 

类加载器的委托机制

当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。

而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

 

自定义类加载器

由于一些特殊的需求,我们可能需要定制ClassLoader的加载行为,这时候就需要自定义ClassLoader了。自定义ClassLoader需要继承ClassLoader抽象类,重写findClass方法。这个方法定义了ClassLoader查找class的方式。因为类加载器是基于委托机制,它会自动向父类加载器委托,如果父类没有找到,就会再去调用我们重写的findClass方法加载。          

1.定义一个需要被加载的类

1 import java.util.Date;
2 public class ClassTest extends Date{
3     public Sting toSring() {
4         return"hello,java!";
5     }
6 }

 

2.定义一个用来加密字节码的类

 1 import java.io.FileInputStream;
 2 import java.io.FileOutputStream;
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 import java.io.OutputStream;
 6 
 7 public class ClassEncrypt extends MyClassLoader{
 8     public static void main(String[] args) throws IOException {
 9         //要加密的字节码文件路径
10         String srcPath="F:\\Workspaces\\MyEclipse 10\\classload\\bin\\test_classload\\ClassTest.class";
11         //加密之后输出的字节码文件路径
12         String destPath="F:/ClassTest.class";
13         FileInputStream fis=new FileInputStream(srcPath);
14         FileOutputStream ofs=new FileOutputStream(destPath);
15         cypher(fis, ofs);
16         fis.close();
17         ofs.close();
18     }   
19     //按字节异或
20     private static void cypher(InputStream in,OutputStream out) throws IOException{
21         int b=-1;
22         while((b=in.read())!=-1){
23             out.write(b^0xff);
24         }
25     }
26 }

3.自定义类加载器

 1 import java.io.ByteArrayOutputStream;
 2 import java.io.FileInputStream;
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.OutputStream;
 8 
 9 public class MyClassLoader extends ClassLoader{
10     
11     @SuppressWarnings("deprecation")
12     @Override
13     protected Class findClass(String name) throws ClassNotFoundException {
14         try {
15             //需要加载的.class字节码的位置
16             String classPath="D:/ClassTest.class";
17             
18             FileInputStream fis=new FileInputStream(classPath);
19             ByteArrayOutputStream bos=new ByteArrayOutputStream();
20             cypher(fis, bos);
21             fis.close();
22             byte[] bytes=bos.toByteArray();
23             return defineClass(bytes, 0, bytes.length);
24         } catch (FileNotFoundException e) {
25             e.printStackTrace();
26         } catch (IOException e) {
27             e.printStackTrace();
28         }
29         return super.findClass(name);
30     }
31     
32     private static void cypher(InputStream in,OutputStream out) throws IOException{
33         int b=-1;
34         while((b=in.read())!=-1){
35             out.write(b^0xff);
36         }
37     }
38 }

4.测试类

 1 import java.lang.reflect.Method;
 2 import java.util.Date;
 3 
 4 public class Test {
 5     public static void main(String[] args) throws Exception {
 6         Class clazz=new MyClassLoader().loadClass("test_classload.ClassTest");
 7         //父类引用
 8         Date d1 =  (Date)clazz.newInstance();
 9         System.out.println(d1);
10         //反射调用
11         Method m1 = Class.forName("test_classload.ClassTest").getMethod("toString", null);
12         System.out.println(m1.invoke(Class.forName("test_classload.ClassTest").newInstance(), null));
13         
14     }
15 }

 

 

 

posted on 2013-08-30 17:25  xscn  阅读(195)  评论(0编辑  收藏  举报