【自学习】插件化加载jar

背景:

  1. 经常存在一种场景:先定义接口,然后针对接口有不同的实现。具体使用哪种实现,最后由业务来定
  2. 我们期望定义的接口,在上传一个jar包后,就能加载jar包中对应的实现,并且功能切换到这种实现上
  3. 实现可以交给第三方来实现,第三方按照接口实现后,拿到我们系统加载使用即可

总结:需要一种即插即用的类加载

主角:ServiceLoader

ServiceLoader可以加载类实现,需要如下步骤:

  • 定义接口
  • 定义实现
  • 定义META-INF/services 并在该目录下配置接口的实现类
  • 使用ServiceLoader加载

分3个模块分别定义了接口,实现1和实现2.并且在定义一个模块用于加载接口实现。

接口
package com.zhangbo.hello;

public interface SayHello {
    String execute(String name);
}
实现1
package com.zhangbo;

import com.zhangbo.hello.SayHello;

public class ChineseSimple implements SayHello {

    @Override
    public String execute(String name) {
        return "优美的中国话";
    }
}
实现2
package com.zhangbo;

import com.zhangbo.hello.SayHello;

/**
 * Hello world!
 *
 */
public class English implements SayHello {

    @Override
    public String execute(String name) {
        return "hello, welcom to en," + name;
    }
}
main模块

也实现了一种

package com.zhangbo.hello;

public class Default implements SayHello {

    @Override
    public String execute(String name) {
        return name;
    }
}

main模块的META-INF/services目录下定义文件:com.zhangbo.hello.SayHello

文件内容

com.zhangbo.hello.Default

main函数如下:

ServiceLoader<SayHello> loads = ServiceLoader.load(SayHello.class);
loads.forEach(e -> {
    System.out.println(e.execute("23456tygh"));
});
System.out.println("finish");

控制台打印:

23456tygh
finish

如果我们想加载英文或者中文版的实现。首先需要把对应的class加载进来,然后在com.zhangbo.hello.SayHello文件中增加对应类名。
幸运的是 META-INF是可以每个jar单独定义的。因此可以有这样2种思路:
首先在中文,英文实现的resouces下面增加 META-INF/servicescom.zhangbo.hello.SayHello 文件内容分别是

com.zhangbo.ChineseSimple
com.zhangbo.English

然后有2种思路:1种是将对应jar包通过pom引入,这种类似mysql-connect-java的jar加载。另一种是通过ClassLoader加载

File dir = new File("D:\\zhangbo-workspace\\zhangbo\\dynamic-load");
File[] files = dir.listFiles();
List<URL> jars = Arrays.stream(files).filter(file -> file.getName().endsWith("jar")).map(file -> {
    try {
        return new URL("file:" + file.getAbsolutePath());
    } catch (MalformedURLException e) {
        return null;
    }
}).filter(Objects::nonNull).collect(Collectors.toList());
URL[] jarArr = new URL[jars.size()];
jarArr = jars.toArray(jarArr);
URLClassLoader classLoader = new URLClassLoader(jarArr);
ServiceLoader<SayHello> loads = ServiceLoader.load(SayHello.class, classLoader);
loads.forEach(e -> {
    System.out.println(e.execute("23456tygh"));
});
System.out.println("finish");

控制台打印

23456tygh
优美的中国话
hello, welcom to en,23456tygh
finish
因此一个简单插件加载工具方法就出来了
/**
 * 加载文件系统中某个目录下接口实现对应的jar包
 * 
 * @param jarDir
 * @param clazz
 * @return
 * @param <T>
 */
public static <T> ServiceLoader<T> loadByFile(String jarDir, Class<T> clazz) {
    File dir = new File(jarDir);
    if (!dir.exists() || !dir.isDirectory()) {
        return ServiceLoader.load(clazz);
    }

    List<URL> jars = Arrays.stream(dir.listFiles()).filter(file -> file.getName().endsWith("jar")).map(file -> {
        try {
            return new URL("file:" + file.getAbsolutePath());
        } catch (MalformedURLException e) {
            return null;
        }
    }).filter(Objects::nonNull).collect(Collectors.toList());
    URL[] jarArr = new URL[jars.size()];
    jarArr = jars.toArray(jarArr);
    URLClassLoader classLoader = new URLClassLoader(jarArr);
    return ServiceLoader.load(clazz, classLoader);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值