转载ClassLoader类加载器

13年前
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
 
 
bootstrap classloader:引导(也称为原始)类加载器它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:
  1. URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();   
  2. for (int i = 0; i < urls.length; i++) {   
  3.     System.out.println(urls[i].toExternalForm());   
  4. }   

 因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库
extension classloader -扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的。
 
system classloader - 系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径。
可以通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。

classloader 加载类用的是全盘负责和委托机制。

全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。
委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找
类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。
 
给一个class文件进行简单的加密,加密后人家对其反编译,也获取不了我们的源代码
 写一个简单需要加密的类,这里继承一个Date,为什么要继续一个父类。因为后面需要解密的class已经被打乱了,所以在解密的时候你需要反射然后强行转换class的时候也是强行转换为一个被加密的class这是会出错,但是你继承了一个父类后你就可以强行转换为一个父类这样子就不会有错误了
  1. <span style="font-size:16px;">public class ClassLoaderAttachment extends Date {  
  2.     public String toString(){  
  3.         return "baby goodnight";  
  4.     }   
  5. }</span>  

 
分别InputStream 指定了需要加密的文件 ,OutputStream加密后输出的文件
  1. <span style="font-size:16px;">    private static void cypher(InputStream ips ,OutputStream ops) throws Exception{  
  2.         int b = -1;  
  3.         while((b=ips.read())!=-1){  
  4.             ops.write(b ^ 0xff);  
  5.         }  
  6.     }</span>  

 加密后给这个class人家使用的时候是没法使用的,会出现Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 这样子的异常除非人家知道你的解密方法 将转换为正确的class ,另外一个方法是通过extends ClassLoader 覆写findClass(String name)的方法来实现 自定义一个classLoader 然后通过我们的classloader去使用这个文件
  1. @Override  
  2.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  3.         // TODO Auto-generated method stub   
  4.         String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".class";  
  5.         try {  
  6.             FileInputStream fis = new FileInputStream(classFileName);  
  7.             ByteArrayOutputStream  bos = new ByteArrayOutputStream();  
  8.             cypher(fis,bos);//对其进行解密   
  9.             fis.close();  
  10.             System.out.println("aaa");  
  11.             byte[] bytes = bos.toByteArray();  
  12.             return defineClass(bytes, 0, bytes.length);  
  13.         } catch (Exception e) {  
  14.             // TODO Auto-generated catch block   
  15.             e.printStackTrace();  
  16.         }  
  17.         return null;  
  18.     }  
使用自定义的ClassLoader使用加密的class可能会出现的异常,父类的加载器会去找原来工程的class 并不是找我们指定的class,把原来工程的class移除掉
第二个可能出现的是 之前在编译的时候 内存中已经保留了原来的jar文件,把eclipse关闭后清空内存在执行 
 
类加载器补充:
在servlet中的时候无法加载httpServlet 把httpServlet 的jar包放到ExtClassLoader管理的目录下就可以了
 
原因是这个样子的 当由ExtClassLoader加载MyServlet的时候先交给他的父类去加载是可以加载成功的时候,MyServlet还需要加载HttpServlet 此时ExtClassLoader的父类没有加载成功于是就由ExtClassLoader加载,加载失败后不会交个WebAppClassLoader加载,所以在用ExtClassLoader加载HttpServlet却没法加载成功!