Java类载入器(二)——本身定义类载入器

  用户定制本身的ClassLoader可以实现如下的一些应用:java

  1. 本身定义路径下查找本身定义的class类文件,或许咱们需要的class文件并不老是在已经设置好的Classpath如下,那么咱们必须想办法来找到这个类,在这样的清理下咱们需要本身实现一个ClassLoader。

  2. 确保安全性:Java字节码很是easy被反编译,对咱们本身的要载入的类作特殊处理,如保证经过网络传输的类的安全性。可以将类通过加密后再传输,在加密到JVM以前需要对类的字节码在解密。这个过程就可以在本身定义的ClassLoader中实现。

  3. 实现类的热部署:可以定义类的实现机制。假设咱们可以检查已经载入的class文件是否被改动,假设改动了。可以又一次载入这个类。

  findClass()的功能是找到class文件并把字节码载入到内存中。本身定义的ClassLoader通常覆盖改方法。以便使用不一样的载入路径,而后调用defineClass()解析字节码。安全


  defineClass()方法用来将byte字节流解析成JVM可以识别的Class对象。markdown

有了这种方法意味着咱们不单单可以经过class文件实例化对象。还可以经过其它方式实例化对象,如咱们经过网络接收到一个类的字节码,拿这个字节码流直接建立类的Class对象形式实例化对象。网络


  本身定义的载入器可以覆盖方法loadClass()以便定义不一样的载入机制。
  假设本身定义的载入器仅覆盖了findClass(),而未覆盖loadClass(即载入规则同样,但载入路径不一样);则调用getClass().getClassLoader()返回的仍然是AppClassLoader!因为真正的load类,仍是AppClassLoader.ide


载入本身定义路径下的class文件

  如下演示一个方法载入指定路径下(”D:/workspace_jee/JavaTest/src/”)的类文件。post

this

package classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class PathClassLoader extends ClassLoader { private String classPath; public PathClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?

> findClass(String name) throws ClassNotFoundException { byte[] classData = getData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getData(String className) { String path = classPath + File.separatorChar+className.replace('.', File.separatorChar)+".class"; try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int num = 0; while((num = is.read(buffer))!=-1) { stream.write(buffer,0,num); } return stream.toByteArray(); } catch(IOException e) { e.printStackTrace(); } return null; } public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader pcl = new PathClassLoader("D:/workspace_jee/JavaTest/src/"); Class c = pcl.loadClass("classloader.SingleClass"); System.out.println(c.newInstance()); } }

  输出结果:classloader.SingleClass@22a7fdef加密


载入本身定义格式的class文件

  假设咱们从网路上下载一个class文件的字节码,但是为了安全性在传输以前对这个字节码进行了简单的加密处理,而后再经过网络传输。spa

当client接收到这个类的字节码后需要通过解密才干还原成原始的类格式。而后再经过ClassLoader的defineClass()方法建立这个类的实例,最后完毕类的载入工做。code


  比方上面的代码中,在获取到字节码(byte[] classData = getData(name);)以后再经过一个类似如下的代码:

private byte[] deCode(byte[] src){
        byte[] decode = null;
        //do something niubility! 精密解码过程
        return decode; 
    }

  将字节码解码成所需要的字节码就能够。


实现类的热部署

  JVM默认不能热部署类,因为载入类时会去调用findLoadedClass(),假设类已被载入,就不会再次载入。
  JVM推断类是否被载入有两个条件:完整类名是否同样,ClasssLoader是不是同一个
  因此要实现热部署的话,仅仅需要使用ClassLoader的不一样实例来载入。


  假设用同一个ClassLoader实例来载入同一个类,则会抛出LinkageError.
  Jsp就是一个热部署的样例。
  例如如下所看到的:

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ClassReloader extends ClassLoader {
    private String classPath;
    String classname = "classloader.SingleClass";

    public ClassReloader(String classpath)
    {
        this.classPath = classpath;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException{
        byte [] classData = getData(name);
        if(classData == null)
        {
            throw new ClassNotFoundException();
        }
        else
        {
            return defineClass(classname,classData,0,classData.length);
        }
    }

    private byte[] getData(String className)
    {
        String path = classPath+className;
        try
        {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while((num = is.read(buffer))!=-1)
            {
                stream.write(buffer,0,num);
            }
            return stream.toByteArray();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args)
    {
        try
        {
            String path = "D:/workspace_jee/JavaTest/src/classloader/";
            ClassReloader reloader = new ClassReloader(path);
            Class r = reloader.findClass("SingleClass.class");
            System.out.println(r.newInstance());
// ClassReloader reloader2 = new ClassReloader(path);
            Class r2 = reloader.findClass("SingleClass.class");
            System.out.println(r2.newInstance());
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
    }
}

  这段代码的执行结果为:

java.lang.LinkageError: loader (instance of  classloader/ClassReloader): attempted  duplicate class definition for name: "classloader/SingleClass"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at classloader.ClassReloader.findClass(ClassReloader.java:26)
    at classloader.ClassReloader.main(ClassReloader.java:62)

  比較两个类是否“相等”,仅仅有在这两个类是由同一个类载入器载入的前提下才有意义。不然。即便这两个类来源于同一个Class文件,被同一个虚拟机载入,仅仅要载入他们的类载入器不一样,那这两个类就必然不相等。

相关文章
相关标签/搜索