我们都知道类加载器是采用双亲委派模型工作的,我们来自定义一个类加载器,来实现一个小的案例(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.class
com.jvm.classloader.ClassLoaderAttachment
com.jvm.classloader.DecodeClassLoader@6d6f6e28
ClassLoaderAttachment Say Hello !