Implement a custom class loader

One o'clock eye

1 ClassLoader class has the following two key ways:

loadClass (String name, boolean resolve): This method is ClassLoader entry point to load the class specified binary name, the system calls the method is to acquire ClassLoader Class object corresponding to the specified class.

findClass (String name): based on binary name to find the class.

If you want a custom ClassLoader, can be achieved by rewriting the above two methods, of course, we recommend rewriting findClass () method, rather than rewrite the loadClass () method.

2 custom class loader common functions

  • Automatic verification code is executed before the digital signature.

  • According to a user-supplied password to decrypt the code, thereby enabling the code obfuscator to prevent decompiling class files.

  • The user needs to dynamically load classes.

  • The application needs to be loaded into other data applications in the form of bytecode.

Two combat

1 CompileClassLoader.java

import java.io.*;
import java.lang.reflect.*;

public class CompileClassLoader extends ClassLoader
{
   // 读取一个文件的内容
   private byte[] getBytes(String filename)
         throws IOException
   {
      File file = new File(filename);
      long len = file.length();
      byte[] raw = new byte[(int)len];
      try(
            FileInputStream fin = new FileInputStream(file))
      {
         // 一次读取class文件的全部二进制数据
         int r = fin.read(raw);
         if(r != len)
            throw new IOException("无法读取全部文件:"
                  + r + " != " + len);
         return raw;
      }
   }
   // 定义编译指定Java文件的方法
   private boolean compile(String javaFile)
         throws IOException
   {
      System.out.println("CompileClassLoader:正在编译 "
            + javaFile + "...");
      // 调用系统的javac命令
      Process p = Runtime.getRuntime().exec("javac " + javaFile);
      try
      {
         // 其他线程都等待这个线程完成
         p.waitFor();
      }
      catch(InterruptedException ie)
      {
         System.out.println(ie);
      }
      // 获取javac线程的退出值
      int ret = p.exitValue();
      // 返回编译是否成功
      return ret == 0;
   }
   // 重写ClassLoader的findClass方法
   protected Class<?> findClass(String name)
         throws ClassNotFoundException
   {
      Class clazz = null;
      // 将包路径中的点(.)替换成斜线(/)。
      String fileStub = name.replace("." , "/");
      String javaFilename = fileStub + ".java";
      String classFilename = fileStub + ".class";
      File javaFile = new File(javaFilename);
      File classFile = new File(classFilename);
      // 当指定Java源文件存在,且class文件不存在、或者Java源文件
      // 的修改时间比class文件修改时间更晚,重新编译
      if(javaFile.exists() && (!classFile.exists()
            || javaFile.lastModified() > classFile.lastModified()))
      {
         try
         {
            // 如果编译失败,或者该Class文件不存在
            if(!compile(javaFilename) || !classFile.exists())
            {
               throw new ClassNotFoundException(
                     "ClassNotFoundExcetpion:" + javaFilename);
            }
         }
         catch (IOException ex)
         {
            ex.printStackTrace();
         }
      }
      // 如果class文件存在,系统负责将该文件转换成Class对象
      if (classFile.exists())
      {
         try
         {
            // 将class文件的二进制数据读入数组
            byte[] raw = getBytes(classFilename);
            // 调用ClassLoader的defineClass方法将二进制数据转换成Class对象
            clazz = defineClass(name,raw,0,raw.length);
         }
         catch(IOException ie)
         {
            ie.printStackTrace();
         }
      }
      // 如果clazz为null,表明加载失败,则抛出异常
      if(clazz == null)
      {
         throw new ClassNotFoundException(name);
      }
      return clazz;
   }
   // 定义一个主方法
   public static void main(String[] args) throws Exception
   {
      // 如果运行该程序时没有参数,即没有目标类
      if (args.length < 1)
      {
         System.out.println("缺少目标类,请按如下格式运行Java源文件:");
         System.out.println("java CompileClassLoader ClassName");
      }
      // 第一个参数是需要运行的类
      String progClass = args[0];
      // 剩下的参数将作为运行目标类时的参数,
      // 将这些参数复制到一个新数组中
      String[] progArgs = new String[args.length-1];
      System.arraycopy(args , 1 , progArgs
            , 0 , progArgs.length);
      CompileClassLoader ccl = new CompileClassLoader();
      // 加载需要运行的类
      Class<?> clazz = ccl.loadClass(progClass);
      // 获取需要运行的类的主方法
      Method main = clazz.getMethod("main" , (new String[0]).getClass());
      Object[] argsArray = {progArgs};
      main.invoke(null,argsArray);
   }
}

2 Hello.java

public class Hello
{
   public static void main(String[] args)
   {
      for (String arg : args)
      {
         System.out.println("运行Hello的参数:" + arg);
      }
   }
}

3 runs

E:\Java\疯狂java讲义\codes\18\18.2>java CompileClassLoader Hello 自定义加载器
CompileClassLoader:正在编译 Hello.java...
运行Hello的参数:自定义加载器

 

Guess you like

Origin blog.csdn.net/chengqiuming/article/details/94961162