我们都知道类加载器是采用双亲委派模型工作的,我们来自定义一个类加载器,来实现一个小的案例(Class文件的解密加载过程) 来深化对ClassLoader加载机制的理解
首先我们要编写一个解码(解密)类加载器(DecodeClassLoader) 实现对指定Class的加载
首先我们先了解下classloader的基础知识:
1.自定义类加载器必须继承ClassLoader。
2.了解ClassLoader中的loadClass方法 , findClass方法 , 以及defineClass方法。
然后我们查看源码
public Class loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
因为自定义ClassLoader继承自ClassLoader类,所以继承了ClassLoader的loadClass方法,该方法调用过程是这样的:
1.首先查找该加载器是否已经加载过这个类,如果加载过直接返回。
2.然后判断是否有父类加载器 如果有则父加载器对类进行加载,或者调用bootstrap加载器对类进行加载
3.如果父加载器及bootstrap加载器都没找到指定的类,那么调用当前类加载器的findClass方法来完成对类的加载
接下来我们来完成自定义类加载器的实现,首先我们定义需要被加密二进制文件的源文件类
/** * Created with IntelliJ IDEA. * Description: 被加密class文件的源文件 * User: zhubo * Date: 2018-03-24 * Time: 10:10 */public class ClassLoaderAttachment { public void say(){ System.out.println("ClassLoaderAttachment Say Hello !"); }}
然后定义加解密工具类,完成加密文件的生成:
/** * Created with IntelliJ IDEA. * Description: 加解密工具类 及 生成加密文件 * User: zhubo * Date: 2018-03-24 * Time: 10:25 */public class GenerateEncryp { public static void cypher(InputStream ips , OutputStream ops) throws IOException { int b = -1; while((b = ips.read()) != -1){ ops.write(b ^ 0xff); } } public static void uncypher(InputStream ips , OutputStream ops) throws IOException { int b = -1; while((b = ips.read()) != -1){ ops.write(b&0xff ); } } /** * 现在要调用加密的类,对某个class文件进行加密. * 那么要传递一个要加密文件及路径,和要保存加密文件的路径 * @param args */ public static void main(String[] args) throws IOException{ // 源文件路径 String srcPath = "D:\\workspace\\quartz_dir\\algorithm\\target\\classes\\com\\jvm\\classloader\\ClassLoaderAttachment.class"; // 目标文件的目录 String descDir = "D:\\workspace"; // 目标文件名称 String descFileName = srcPath.substring(srcPath.lastIndexOf("\\")); String descPath = descDir + descFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(descPath); cypher(fis,fos); fis.close(); fos.close(); }}
我们执行main函数 会在D://workspace 下生成ClassLoaderAttachment.class 文件的加密文件,接下来我们要完成对加密文件的加载工作。
首先我们自定义类加载器DecodeClassLoader 继承ClassLoader: 其中我们沿用父类的loadClass方法,同样采用双亲委派模型来加载类。但是需要覆盖findClass方法,在findClass方法中实现对类的加载过程:
/** * Created with IntelliJ IDEA. * Description: * User: zhubo * Date: 2018-03-24 * Time: 9:40 */public class DecodeClassLoader extends ClassLoader { private String pathDir; public DecodeClassLoader(String pathDir) { this.pathDir = pathDir; } @Override protected Class findClass(String name) throws ClassNotFoundException { String classFileName = pathDir + "\\" + name.substring(0,name.lastIndexOf(".")) + ".class"; System.out.println(classFileName); try{ FileInputStream fis = new FileInputStream(classFileName); ByteArrayOutputStream bos = new ByteArrayOutputStream(); GenerateEncryp.cypher(fis,bos); //GenerateEncryp.uncypher(fis,bos); fis.close(); byte[] bytes = bos.toByteArray(); return defineClass(null,bytes,0,bytes.length); }catch (Exception e){ e.printStackTrace(); } return super.findClass(name); }}
然后我们对经过加密的.class文件进行类的加载过程:
/** * Created with IntelliJ IDEA. * Description: 编解码调度类 * User: zhubo * Date: 2018-03-24 * Time: 10:17 */public class ClassLoaderTest { public static void main(String[] args) throws Exception{ DecodeClassLoader dcl = new DecodeClassLoader("D:\\workspace"); Class aClass = dcl.loadClass("ClassLoaderAttachment.class"); Object o = aClass.newInstance(); System.out.println(o.getClass().getName()); System.out.println(o.getClass().getClassLoader()); aClass.getMethod("say",null).invoke(o,null); }}
输出:
D:\workspace\ClassLoaderAttachment.classcom.jvm.classloader.ClassLoaderAttachmentcom.jvm.classloader.DecodeClassLoader@6d6f6e28ClassLoaderAttachment Say Hello !