前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于ClassLoader还可以这么做

关于ClassLoader还可以这么做

作者头像
PhoenixZheng
发布2018-08-07 16:41:32
2860
发布2018-08-07 16:41:32
举报
庖丁解牛--自定义ClassLoader

上次我们说到Java的双亲委托机制,导致我们不能加载到WangHouse里的Socker类。今天我们来说说怎么在不改变目录结构的情况下加载Socker。

自定义ClassLoader

自定义ClassLoader可能比较陌生。既然我们知道AppClassLoader是对应当前的classpath,而ClassLoader会根据class path来加载类,那么我们可以再自定义一个ClassLoader,将它的路径指定到WangHouse。 自定义ClassLoader分两步 · 继承 java.lang.ClassLoader · 重写 findClass方法

这里需要说一下为什么只重写findClass方法。JDK在搜索类的时候,会先调用 loadClass,在找不到的时候会调用 findClass,虽然重写loadClass()方法也可以,但是这样会改写原有的正常查找逻辑,因此我们只需要重写findClass()就可以,这是Java预留给开发者的可重载方法。

public class DiskClassLoader extends ClassLoader {

    private String mFilePath;

    public DiskClassLoader(String mFilePath) {
        this.mFilePath = mFilePath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String fileName = getFileName(name);
        File file = new File(mFilePath, fileName);
        try {
            FileInputStream ins = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            try {
                while((len = ins.read()) != -1) {
                    bos.write(len);
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
            byte[] data = bos.toByteArray();
            ins.close();
            bos.close();
            return defineClass(name, data, 0, data.length);
        } catch (IOException exp) {
            exp.printStackTrace();
        }
        return super.findClass(name);
    }

    private String getFileName(String name){
        int index = name.lastIndexOf('.');
        if(index == -1) {
            return name + ".class";
        } else {
            return name.substring(index) + ".class";
        }
    }
}

简单的解释代码的逻辑是在传进去的路径下查找对应的class文件,把它转换为文件流,通过ClassLoader的 defineClass方法获得Class对象。

用自定义Loader来加载class

现在我们有可以找class的工具了,目标的类Socker在隔壁WangHouse,调用的代码这么写

public class Ming {

  public static void main(String[] args) {
    System.out.println("Ming looking for socker");
    findSocker();
  }

  private static void findSocker() {
    DiskClassLoader loader = new DiskClassLoader("../WangHouse/");
    try {
      Class b = loader.loadClass("Socker");
      if(b != null){
        try {
          Object object = b.newInstance();
          Method method = b.getDeclaredMethod("callSockerMethod", null);
          method.invoke(object, null);
        } catch(Exception e) {
          e.printStackTrace();
        }
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

运行结果

$ java Ming Ming looking for socker Socker method invoke

看到这里肯定会有疑问,为什么要用反射。我们接着说。

举一反三

为什么要用反射来获取Socker对象?既然都能找到类,也已经加载进来了,为何不直接 new 就完了?

其实很简单,因为Ming和Socker并不在同一个ClassLoader里,Ming在AppClassLoader中,而Socker我们加载到了DiskClassLoader里。子Loader加载类的时候可以去父Loader里找到,就像我们调用System,而父Loader用子Loader里的类的时候是不行的,只能用反射。

…可能有人已经意识到,Android的热修复是不是可以用自定义ClassLoader来做? 没错!有些热修复方案就是基于ClassLoader做的。 明天我们接着讲开发中时常遇到的ClassCastException。在我们准备好相关的点之后,就可以自己来写一个热修复框架了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 庖丁解牛--自定义ClassLoader
  • 自定义ClassLoader
  • 用自定义Loader来加载class
  • 举一反三
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档