概述
引言
插件式的架构可以为系统带来极高的扩展性。典型的一个例子就是eclipse。我们可以下载各种各样的插件来不断丰富eclipse的功能,而eclipse本身却不需要作任何改动。那么在java中如何实现插件机制呢?
动态加载
实现插件式系统的基础是动态加载机制。所谓动态加载是指系统所要用到的字节码文件不需要添加classpath目录下,而在运行时由程序本身根据需要加载到jvm中。这种情况下相应的jar包可以放在任意位置,甚至从网络上获取。jdk中的ClassLoader类为我们提供了这一强大的特性。我们可以自定义一个子类来继承ClassLoader类,从而实现一些自定义的需求,但不要轻易就重写ClassLoader的方法,除非你对ClassLoader非常熟悉,并且有非这样做不可的需求。在本文,我们可以直接使用了jdk自带的URLClassLoader类来实现插件的动态加载。
URLClassLoader的构造函数原型为:
public URLClassLoader(URL[] urls, ClassLoader parent)
第一个参数为class文件或jar包的URL列表,大致可以理解为class文件的地址,该地址可以是本地磁盘地址也可以是网络地址。第二个参数为父级ClassLoader,该参数我们一般都赋值为调用者本身的ClassLoader。此处需要非常注意的是java中的类都是由classloader加载的,如果同一个class文件由不同的classloader加载,则被认为是两个class类型,他们的实例间也不能强制转换。关于ClassLoader的更详尽的讲解可以参考这篇博文: 深入分析Java ClassLoader原理
动态加载类文件需要使用URLClassLoader类的loadClass方法,其原型为:
public Class<?> loadClass(String name) throws ClassNotFoundException;
其参数的含义即为我们想要加载的类的全限定名称。
先上一段示例代码:
//播放器插件列表
List<Player> audioPlayers = new ArrayList<>();
//动态加载插件
URL pluginJar = new File("D:/plugins/wma-player.jar").toURI().toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{pluginJar}, Thread.currentThread().getContextClassLoader());
Class clazz = classLoader.loadClass("org.cbqin.player.audio.WmaPlayer");
//生成插件实例
Object obj = clazz.newInstance();
if (obj instanceof Player) {
//缓存到插件列表中,供之后播放时调用
audioPlayers.add((Player) obj);
}
该段代码的目的是在运行时动态加载WmaPlayer类,实例化后放入播放组件列表中,从而使得播放器可以播放wma格式的音频。
从中我们不难看出插件加载的一般步骤为:
- 构造URLClassloader实例:通过构造方法或newInstance方法将其所能加载的URL列表设置为我们需要扫描的jar包地址列表
- 加载目标类文件:通过URLClassloader的loadClass方法加载目标类文件。在上述代码中为了方便理解使用了硬编码的形式,而在实际工程中一般是在jar包中添加一个plugin.xml(名字和类型均可以自由设定)文件来描述该包中需要加载的类以及插件的一些其他信息。
- 生成类实例 : 通过反射调用相应方法生成目标类的实例。
热替换
现在,我们已经实现了插件的动态加载。那么能否再给力一点呢,就像tomcat中,我们修改了webapp下面工程的web.xml,则tomcat可以为我们重新部署修改后的工程。
答案是肯定的,热替换的基础是对类文件的实时监控。当然,同时也离不开动态加载。
目前java中的文件监控方案主要有:
- jdk中的新增API WatchService API
- 通过JNI调用底层接口的jnotify
- Apache Commons中的FileAlterationObserver
热替换的基本步骤为:
- 使用文件监控API监视插件目录的变化
- 当插件目录下的文件发生修改时,重新加载相应的插件
示例代码如下:
//监控插件目录
FileAlterationObserver observer = new FileAlterationObserver("D:/plugins/");
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileChange(File file) {
System.out.println(file.getName() + " has been changed");
//重新加载相应的jar包
try {
reloadPlugin(file);
} catch (Exception e) {
e.printStackTrace();
}
}
});
//设置监控间隔
FileAlterationMonitor fileMonitor = new FileAlterationMonitor(1000, observer);
// 启动监控
fileMonitor.start();
结合动态加载和文件监控API,看起来“高大上”的插件机制便被轻松的实现了。后面有空的时候会考虑再写一篇关于插件沙箱机制实现的文章,完善下这个主题。
最后
以上就是甜美曲奇为你收集整理的java中插件机制和热升级的实现方案引言动态加载热替换的全部内容,希望文章能够帮你解决java中插件机制和热升级的实现方案引言动态加载热替换所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复