MichLy

导航

网络类加载器

背景

由于在深入jvm虚拟机中看到了有部分说道class可以通过网络的方式加载,于是就想到了是不是可以通过在网络上发布jar包,然后程序动态加载网络上的jar包(可拓展为热更新)

代码地址

调用模块 https://coding.net/u/mich/p/easytry/git/tree/master/src/com/netclassloader

实现模块 https://coding.net/u/mich/p/easytry/git/tree/master/netlogicImpl

接口模块 https://coding.net/u/mich/p/easytry/git/tree/master/netlogicInterface

说明

该内容主要由三部分组成

  1. 接口模块  主要包含接口jar包
  2. 实现模块     导入接口jar包,然后实现接口的具体逻辑,打包成jar包,最后通过工具包,生成对应的接口文件,将jar包和接口文件都上传至网络,我这里就直接使用了git上的地址,这里还有生成了接口协议文件,类似于双方协议,实现类和调用者的对应关系
  3. 调用模块     自定义一个管理类,下载接口文件与接口jar包,解析jar包中的所有有效class文件,然后将协议文件以及类与class的map存放在成员变量中,当网络加载器加载类指定类时,通过刚才的map获取class文件的byte[],定义该类,管理类同时提供一个获取服务的方法,通过协议文件,将接口与实现类绑定在一起

内容

接口模块

接口模块相对来说比较简单,两个接口

package com.netresource.logic;

/**
 * Created by Mich on 2017/7/22.
 */
public interface ISayHello {
    Object sayHello();
}

  

package com.netresource.logic;

/**
 * Created by Mich on 2017/7/22.
 */
public interface ISayWorld {
    Object sayWorld();
}

实现模块

实现模块需要先导入刚才接口模块,然后写两个接口的实现类

package com.netlogic;

import com.netresource.logic.ISayHello;

/**
 * Created by Mich on 2017/7/22.
 */
public class SayHelloImpl implements ISayHello {
    @Override
    public Object sayHello() {
        return "Hello";
    }
}
package com.netlogic;

import com.netresource.logic.ISayWorld;

/**
 * Created by Mich on 2017/7/22.
 */
public class SayWorldImpl implements ISayWorld {
    @Override
    public Object sayWorld() {
        return "World";
    }
}

你会发现还有两个util,fileutil主要是我一直使用的对文件的一些处理比较方便,就直接引用了,PropertiesUtil主要是协议文件的生成工具,通过传入实现类的包名,然后扫描该包下的所有类把接口和实现做对应关系存放在output.properties文件中,需要注意的是,如果一个接口并不支持有多个实现类,但是一个实现类实现多个接口是可以的

最后将实现类打jar包,以及将协议文件上传至网络,我这里图方便直接放到了coding上https://coding.net/u/mich/p/easytry/git/raw/master/src/com/netclassloader/output/netlogicImpl.jar

https://coding.net/u/mich/p/easytry/git/raw/master/src/com/netclassloader/output/output.properties

调用模块

同样调用模块需要引用接口模块的jar包,然后介绍一下调用模块的具体目录结构(这里无视output文件夹,这主要是我刚才上传的两个文件,jar包和协议文件,为了在一个模块里才放这里,实际不需要这个文件夹)

 

NetClassManager,由于代码有点多就不在这里显示了,具体可以再coding上去看,这里主要介绍一下结构

  1. 构造函数,需要传入两个网络地址,一个是协议文件网络地址,一个是jar包的网络地址
  2. getService通过传入ClassLoader和Class,会根据协议文件的对应关系获得网络jar包上的对应实现类
  3. initProperties初始化协议文件,主要是从网络下载协议文件,然后存放在成员变量中
  4. initImplJar初始化jar包,将jar包暂存在本地,然后解析获得各个内部的class的byte[],通过协议文件,获取所需要的类,存放在classMap成员变量中
  5. getClassMap外部获取对应map,主要是给自定义的classLoader可以获得指定的class的byte[]

NetClassLoader类加载器

package com.netclassloader;

/**
 * Created by Mich on 17/7/17.
 */
public class NetClassLoader extends ClassLoader {
    private NetClassManager netClassManager;

    public NetClassLoader(NetClassManager netClassManager) {
        this.netClassManager = netClassManager;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = netClassManager.getClassMap().get(name);
        return defineClass(name, bytes, 0, bytes.length);
    }
}

 这个就比较简单了,继承了ClassLoader,构造函数传入刚刚说明的管理类NetClassManager,然后重写了findClass的方法,通过管理类的map来获取字节数组

 

Main作为测试的入口函数

   public static void main(String[] args) {
        String jarUrl = "https://coding.net/u/mich/p/easytry/git/raw/master/src/com/netclassloader/output/netlogicImpl.jar";
        String propertiesUrl = "https://coding.net/u/mich/p/easytry/git/raw/master/src/com/netclassloader/output/output.properties";
        NetClassManager netClassManager = new NetClassManager(propertiesUrl, jarUrl);
        NetClassLoader classLoader = new NetClassLoader(netClassManager);
        ISayHello hello = netClassManager.getService(classLoader, ISayHello.class);
        ISayWorld world = netClassManager.getService(classLoader, ISayWorld.class);
        System.out.println(hello.sayHello());
        System.out.println(world.sayWorld());
    }

 最后就是比较简单的调用了,运行结果

 最后

其实这里也只是抛砖,如果具体使用,应该还需要版本控制,可以有一个专门的类,或者手动调用,去获取最新的jar包,和最新的协议接口文件,当然更好一点可以再添加一个实现类的版本控制,这样就需要修改的粒度更小了。对了如果要添加动态更新还需要修改NetClassLoader类,现在目前只是第一次加载处理,如果有更新,就需要重写loadClass方法了。最后想想其实协议文件,和版本控制可以直接放在jar包中。。。等下次再继续改进吧。。。

posted on 2017-07-23 15:01  MichLy  阅读(455)  评论(0编辑  收藏  举报