1.基础使用ClassLoader
1.1 创建自定义ClassLoader并继承ClassLoader
这里我只覆写了一个findClass方法(实际上可以覆写更多方法)因为在下文我需要达到加密class,需要覆写该findClass(),在该findClass里,调用了我自己写的一个method:getClassByte(),该方法的作用是读取class文件字节流,那么我们的加密手段,可以在该方法中做文章哈。当classData为空的时候,我们就交给super的方法处理(抛出ClassNotFound的异常),当classData不为空,那么我们就加载该类。
package classload;
import java.io.*;
/**
* @author Lczy-Huang
* @classname MyClassLoader
* @desc 自定义类加载器
* @date 2020-05-24 20:38
**/
public class MyClassLoader extends ClassLoader {
private String classPath;
private final static int BUFSIZE = 4096;
public MyClassLoader(String classPath){
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException{
try{
byte[] classData = getClassByte(name);
if(classData!=null){
return defineClass(name,classData,0,classData.length);
}
}catch (IOException e){
e.printStackTrace();
}
return super.findClass(name);
}
/***
* class字节流获取
*/
private byte[] getClassByte(String className) throws IOException{
String classRealPath = classPath + File.separatorChar + className.replace('.',File.separatorChar)+".class";
byte[] buf = new byte[BUFSIZE];
int len;
ByteArrayOutputStream out = null;
try(InputStream in = new FileInputStream(classRealPath);){
out = new ByteArrayOutputStream();
while ((len = in.read(buf))!=-1){
out.write(buf,0,len);
}
}catch (FileNotFoundException ex){
ex.printStackTrace();
}finally {
if(out!=null){
out.close();
}
}
if(out!=null){
return out.toByteArray();
}
return null;
}
}
2.ClassLoader测试
测试主类中,使用该类加载器进行加载我已经预先编译好的class文件,该文件不要存在运行项目之下,会导致系统加载器加载该文件,测试主类:
package classload;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Lczy-Huang
* @classname MyClassLoaderTest
* @desc 测试类加载
* @date 2020-05-24 20:59
**/
public class MyClassLoaderTest {
public static void main(String []args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//自定义类加载器的加载路径
MyClassLoader myClassLoader=new MyClassLoader("C:\\Users\\Lczy-Huang\\Desktop");
//包名+类名
Class c=myClassLoader.loadClass("classload.MyClassLoaderTestClass");
if(c!=null){
Object obj=c.newInstance();
Method method=c.getMethod("test", null);
method.invoke(obj, null);
System.out.println(c.getClassLoader().toString());
}
}
}
可以看出MyClassLoader已经生效了,运行结果如下:
This is success
classload.MyClassLoader@12bb4df8
3. ClassLoader改造Secuity ClassLoader
3.1 加密解密类
这里只编写单字节的加密,所以一次读取一个字节,而后异或一个数作为加密。
package classload;
import java.io.*;
/**
* @author Lczy-Huang
* @classname EncrypClassFile
* @desc 加密class文件为eclass文件(自定义eclass区分class)
* @date 2020-05-24 23:07
**/
public class EncrypClassFile {
public static void encrypt(String src, String dest) throws IOException {
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);) {
int c = -1;
while ((c = in.read()) != -1) {
// 进行单字节加密
out.write(encryptAlgorithm(c));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/***
* 单字节加密算法
*/
public static int encryptAlgorithm(int c){
return c ^ 0xff;
}
/***
* 单字节解密算法
*/
public static int decryptAlgorithm(int c){
return c ^ 0xff;
}
public static void main(String[] args) throws IOException {
encrypt("C:\\Users\\Lczy-Huang\\Desktop\\classload\\MyClassLoaderTestClass.class"
,"C:\\Users\\Lczy-Huang\\Desktop\\classload\\MyClassLoaderTestClass.eclass");
}
}
3.2 ClassLoader类改造
主要改造的是:getClassByte(),从每次获取BUFSIZE大小的数组变成每次获取单字节,而后进行解密,拼接的文件后缀,由于加密文件为eclass,所以这里也需要变成eclass。
package classload;
import java.io.*;
/**
* @author Lczy-Huang
* @classname MyClassLoader
* @desc 自定义类加载器
* @date 2020-05-24 20:38
**/
public class MyClassLoader extends ClassLoader {
private String classPath;
private final static int BUFSIZE = 4096;
public MyClassLoader(String classPath){
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException{
try{
byte[] classData = getClassByte(name);
if(classData!=null){
return defineClass(name,classData,0,classData.length);
}
}catch (IOException e){
e.printStackTrace();
}
return super.findClass(name);
}
/***
* class字节流获取
*/
private byte[] getClassByte(String className) throws IOException{
String classRealPath = classPath + File.separatorChar + className.replace('.',File.separatorChar)+".eclass";
byte[] buf = new byte[BUFSIZE];
int c;
ByteArrayOutputStream out = null;
try(InputStream in = new FileInputStream(classRealPath);){
out = new ByteArrayOutputStream();
while ((c = in.read())!=-1){
out.write(EncrypClassFile.decryptAlgorithm(c));
}
}catch (FileNotFoundException ex){
ex.printStackTrace();
}finally {
if(out!=null){
out.close();
}
}
if(out!=null){
return out.toByteArray();
}
return null;
}
}
3.3 测试效果
package classload;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Lczy-Huang
* @classname MyClassLoaderTest
* @desc 测试类加载
* @date 2020-05-24 20:59
**/
public class MyClassLoaderTest {
public static void main(String []args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//自定义类加载器的加载路径
MyClassLoader myClassLoader=new MyClassLoader("C:\\Users\\Lczy-Huang\\Desktop");
//包名+类名
Class c=myClassLoader.loadClass("classload.MyClassLoaderTestClass");
if(c!=null){
Object obj=c.newInstance();
Method method=c.getMethod("test", null);
method.invoke(obj, null);
System.out.println(c.getClassLoader().toString());
}
}
}
运行结果:
This is success
classload.MyClassLoader@12bb4df8