Java实现的自定义类加载器示例

 更新时间:2019年07月17日 08:48:51   作者:cakincqm  
这篇文章主要介绍了Java实现的自定义类加载器,结合具体实例形式分析了java自定义类加载器的原理与具体实现技巧,需要的朋友可以参考下

本文实例讲述了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 自定义加载器
CompileClassLoader:正在编译 Hello.java...
运行Hello的参数:自定义加载器

更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总

希望本文所述对大家java程序设计有所帮助。

相关文章

  • java实现分页显示效果

    java实现分页显示效果

    这篇文章主要为大家详细介绍了java实现页显示效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08
  • Java设计模式之代理模式与@Async异步注解失效的解决

    Java设计模式之代理模式与@Async异步注解失效的解决

    代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象
    2022-07-07
  • EasyExcel自定义下拉注解的三种实现方式总结

    EasyExcel自定义下拉注解的三种实现方式总结

    使用EasyExcel设置下拉数据时,每次都要创建一个SheetWriteHandler组件确实比较繁琐,为了优化这个过程,我们可以通过自定义注解来简化操作,下面就来看看具体实现方法吧
    2024-10-10
  • Java递归实现迷宫游戏

    Java递归实现迷宫游戏

    这篇文章主要介绍了如何利用Java递归方法实现迷宫游戏,下面文章会详细的从为问题描述开始,清晰的解题思路以及详细的代码实现,具有一定的参考价值,需要的小伙伴可以参考一下
    2021-12-12
  • 详解SpringBoot中使用RabbitMQ的RPC功能

    详解SpringBoot中使用RabbitMQ的RPC功能

    这篇文章主要介绍了详解SpringBoot中使用RabbitMQ的RPC功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Spring干预Bean的生命周期的方法详解

    Spring干预Bean的生命周期的方法详解

    Spring 提供了多种机制让我们能够在 Bean 生命周期的不同节点“插入”自己的逻辑,这些机制可以分为两大类:针对单个 Bean 的干预和针对所有/多个 Bean 的全局干预,本文将通过代码示例给大家详细的介绍一下,需要的朋友可以参考下
    2025-07-07
  • Spring AOP通知类型与实战示例讲解

    Spring AOP通知类型与实战示例讲解

    Spring AOP提供了五种通知类型:@Before、@After、@AfterReturning、@AfterThrowing和@Around,每种通知类型都有其特定的使用场景和实现方式,通过合理使用这些通知类型,可以实现各种横切关注点的模块化和解耦,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • SpringBoot集成免费的EdgeTTS实现文本转语音

    SpringBoot集成免费的EdgeTTS实现文本转语音

    在需要文本转语音(TTS)的应用场景中(如语音助手、语音通知、内容播报等),Java生态缺少类似Python生态的Edge TTS 客户端库,不过没关系,现在可以通过 UnifiedTTS 提供的 API 来调用免费的 EdgeTTS 能力,本文给大家介绍了SpringBoot集成免费的EdgeTTS实现文本转语音
    2025-10-10
  • java后台防止表单重复提交方法详解

    java后台防止表单重复提交方法详解

    这篇文章主要介绍了后台防止表单重复提交,利用Session防止表单重复提交,判断请求url和数据是否和上一次相同,利用Spring AOP和redis的锁需要的朋友可以参考下
    2022-12-12
  • Java8 List集合如何移除满足条件的元素

    Java8 List集合如何移除满足条件的元素

    这篇文章主要介绍了Java8 List集合如何移除满足条件的元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论