既然类加载器也不过是一个将需要的class文件加载在内存里的普通Java类,(具体参见上一篇博客) 那么我们是否可以通过编写实现自定义Java类加载器呢?答案是肯定的。

     我们参照java.lang包下的ClassLoader来编写自定义加载器。

      首先定义一个Student类,这里是通过对Student这个类生成的class文件加密(在项目的bin目录下)实现绕过系统的ApplicationClassLoader,而是靠自定义的加载器加载,类里没有定义方法(因为可能自己定义的加载器有问题,类无法加载,这样没办法调用该方法),而是重写toString方法,代码如下:

public class Student {

	public String toString(){
        return "学习中";
    }

}


     然后开始新建一个类加载器MyClassLoader 继承自ClassLoader ,这里直接定义main方法进行加密,在 里面定义原来class文件所在路径,和加密后的class文件路径 (我这里是 在项目下新建一个文件夹test_lib,作为自己的class目录)代码如下:

public class MyClassLoder extends ClassLoader {

	public static void main(String[] args) {
		String srcPath="D:\\workbases\\java\\bin\\com\\huaxin\\classLoader\\Student.class";      
		String destPath="test_lib\\Studet.class";
    //加密
 encrypt(srcPath,destPath);
	} 
}


       接着编写加密算法(不是重点,所以写的很简单比较简单,也方便解密)构建文件输入流 ,在读的时候对其加一, 代码如下:

//加密处理 破坏原class,使得ApplicationClassLoader无法加载    
	public static void encrypt(String srcPath,String destPath){
		try {
			InputStream in=new FileInputStream(srcPath);
			OutputStream out=new FileOutputStream(destPath);
			int i;
			while((i=in.read())!=-1){
				out.write(i+1);
			}
			in.close();
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}


    然后运行项目,刷新可以看见在新建的 文件夹test_lib 下,有编译后生成的Student.class 文件 ,

java自定义工具类属性 java 自定义类加载器_加密

将这个文件复制拷贝到系统class文件的目录下,(就是上面的srcPath,将原来的Student.class覆盖掉)这时我们在新建一个Test类,让它去引入Student类,发现程序报错,可见系统的ApplicationClassLoader加载器已经没办法加载这个破坏后的class文件,需要使用自己的加载器进行解密,再加载,继续写自定义的加载器(注意,此时不要再动Student.java文件,不然系统很快重新编译,就无法达到使用自己的加载器的目的)

   然后继续在类加载器MyClassLoader中定义解密算法

//解密处理
		public static void desencrypt(String srcPath,OutputStream out){
			try {
				InputStream in=new FileInputStream(srcPath);
				int i;
				while((i=in.read())!=-1){
					out.write(i-1);   //还原
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}



      借鉴ClassLoader的API,我们知道自定义的加载器需要重写findClass方法, 代码如下:

@Override
		protected Class<?>  findClass(String name) throws ClassNotFoundException{
			//检验是否触发 findClass,将name值传入
			System.out.println("name="+name);
			//拼接传入的路径值
			String srcPath="test_lib\\"+name;
			//将本地文件读到内存中 向内存中输出的流
			ByteArrayOutputStream  baos=new ByteArrayOutputStream();
			  //读到内存后进行还原
			desencrypt(srcPath,baos);
			//得到Student.class在内存中的字节数组
			byte[] buf = baos.toByteArray();
			//将字节数组得到对应的Class实例
			return defineClass(buf,0,buf.length);  
			//return super.findClass(name);  
		}



然后我们在定义的T est文件中再次加载Student类,


public class Test {
	
	public static void main(String[] args) throws Exception {
		//创建自定义加载器对象(最终返回类的字节码)
		Class c=new MyClassLoder ().loadClass("Studet.class");  
	       //直接实例化对象输出  调用toString方法
		System.out.println(c.newInstance().toString());
	}	
}



后台输出:

java自定义工具类属性 java 自定义类加载器_jvm_02

至此,使用自己的类加载器加载到了Student类

    最后说一个尴尬的地方由于我在第一次加密时将保存路径destPath中的“student.class"错写成"Studet.class",解密的时候一直报类找不到异常,我还在纳闷,看了半天发现自己写错了,这真是瞎呀,为了警戒自己,也没有改,直接留下来了(其实我是懒得再截图什么的,O(∩_∩)O),经常犯这样低级的错误,下次要睁大眼睛了。