Java中怎么自定义一个类加载器,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
一 点睛
1 ClassLoader类有如下两个关键方法:
loadClass(String name, boolean resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。
findClass(String name):根据二进制名称来查找类。
如果需要实现自定义的ClassLoader,可以通过重写以上两个方法来实现,当然我们推荐重写findClass()方法,而不是重写loadClass()方法。
2 自定义类加载器常用功能
执行代码前自动验证数字签名。
根据用户提供的密码解密代码,从而可以实现代码混淆器来避免反编译class文件。
根据用户需求来动态地加载类。
根据应用需求把其他数据以字节码的形式加载到应用中。
二 实战
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 运行
E:\Java\疯狂java讲义\codes\18\18.2>java CompileClassLoader Hello
看完上述内容,你们掌握Java中怎么自定义一个类加载器的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。