1.写一个ClassLoaderAttachment类继承Date作为测试用的class文件。
package com.lch.day2;
import java.util.Date;
public class ClassLoaderAttachment extends Date {
public String toString(){
return "hello,itcast";
}
}
2.先写一个自己的类加载器MyClassLoader.java,其中包括解密和加密class文件。
package com.lch.day2;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destFilePath = destDir + "\\" + destFileName;
FileOutputStream fos = new FileOutputStream(destFilePath);
cypher(fis,fos);
fis.close();
fos.close();
}
// 加密算法
private static void cypher(InputStream ips, OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b^0xff);// 异或运算
}
}
private String classDir;
@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + "\\" + name.substring(name.lastIndexOf(".")+1) + ".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
//定义一个字节数组流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);// 解密
fis.close(); // 关闭流
byte[] bytes = bos.toByteArray();// 得到字节数组
System.out.println("aaa");
return defineClass(bytes, 0, bytes.length); // 生成一个字节数组
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
配置运行 run configuration
这样,在bin/com/lch/day2下的就是正常的class文件,而itcastlib下的就是加密之后的class文件。
3.写一个测试类ClassLoaderTest.java,来加载class文件。
package com.lch.day2;
import java.util.Date;
public class ClassLoaderTest{
public static void main(String[] args) throws Exception{
System.out.println(
ClassLoaderTest.class.getClassLoader().getClass().getName()
);
System.out.println(
System.class.getClassLoader()
);
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
//System.out.println(new ClassLoaderAttachment().toString());
Class clazz = new MyClassLoader("itcastlib").loadClass("com.lch.day2.ClassLoaderAttachment");
//Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
Date d1 = (Date)clazz.newInstance();
System.out.println(d1);
}
}
这个类中,执行new MyClassLoader("itcastlib").loadClass("com.lch.day2.ClassLoaderAttachment");时,首先会由BootStrap在jre/lib/rt.jar下寻找com.lch.day2.ClassLoader.Attachment.class.找不到,再由ExtClassLoader在jre/lib/ext/*.jar中寻找com.lch.day2.ClassLoader.Attachment.class.找不到的话,在由AppClassLoader在classpath指定的所有jar或目录中寻找com.lch.day2.ClassLoader/Attachment.class。最后会由AppClassLoader加载该class文件,并且MyClassLoader中的输出语句“aaa”不会输出。
如果我们将bin下的com.lch.day2.ClassLoader.Attachment.class删除,则三个加载器都找不到com.lch.day2.ClassLoader.Attachment.class,就会由我们自己编写的类加载器来加载com.lch.day2.ClassLoader.Attachment.class文件,由于我们自己重写的findClass方法中,做了如下处理String classFileName = classDir + "\\" + name.substring(name.lastIndexOf(".")+1) + ".class";所以MyClassLoader会在itcastlib下找ClassLoaderAttachment.class文件,同时会此class文件做解密操作。
如果我们不删除bin下的那个ClassLoaderAttachment.class文件,而是将其替换成itcastlib中的ClassLoaderAttachment.class文件,就会报错。因为ClassLoaderAttachment.class加密的,而AppClassLoader并没有对其进行解密操作。