Java动态加载与插件开发研究

2015-05-30 09:35钱宇虹
中小企业管理与科技·下旬刊 2015年10期
关键词:插件

摘 要:本文首先研究了Java语言的动态加载方式和实现插件化的方法, 并以支持不同音频格式的音乐播放器为例展示了Java插件的开发步骤。本文所展示的Java插件的开发方法和步骤具有一般性指导意义,可以很好地解决新功能植入或着功能模块定制问题。

关键词:动态加载;Java 插件;功能植入;模块定制

1 概述

软件项目开发经常会遇到这样的情况发生,就是当项目开发结束并交付使用后,需要增加一些新功能。我们通常希望在不改变原有应用程序代码的前提下,将新增加的功能植入到系统中。

还有一种情况就是针对同一个功能模块,不同用户会要求不同的具体实现。例如,呼叫中心坐席软件中的来电弹屏功能,不同用户可能要求展示客户信息的方式不一样,信息来源不一样,有的甚至要求完成一些额外的功能。这就是所谓的功能模块定制。

不管是新功能植入,还是功能模块定制,我们都可以使用插件化技术来解决。新增加的功能模块,或者被定制的功能模块,被

称为插件。插件化技术带来的好处直接体现为降低了模块之间的耦合性,便于各个模块的独立维护,方便和加快整个项目的维护更新。

Java语言的动态加载技术完美的支持了插件化开发。本文首先研究了Java语言的动态加载方式和实现插件化的方法,并以支持不同音频格式的音乐播放器为例展示了Java插件的开发步骤。

2 Java动态加载并实例化的几种方式

Java程序运行时是需要把类加载到内存的。Java类的加载方式有两种:隐式加载和显式加载。隐式加载是通过new的方式,如:Person person=new Person(),在类初始化时由Java虚拟机根据相应的类加载器(ClassLoader)将类载入[1]。显式加载则是程序员在代码中显式地利用某个类加载器将类载入。显示加载并实例化类又可以通过下面两种方法:

2.1 使用Class类[1]

在Java中通过Class类的静态方法forName()可以动态加载一个指定的类(需要提供类的完整名字),然后调用newInstance()就可以构造一个该类的实例,例如:String className = "com.abc.Person";

Person person=Class.forName(className).newInstance();

这个功能对于相同接口的不同实现具有重大意义。我们可以定义一个接口,然后提供好几个该接口的实现类,用户通过修改配置文件来指定实际使用哪一个实现类,然后在源码中读取配置文件,就可以构造一个指定实现类的实例而不用每次修改源码。通过这种方法,系统只要关心接口的定义,用户只要进行配置文件的设置,就完成了同一功能不同实现的任意切换。

2.2 使用ClassLoader

这种方法需要首先获得ClassLoader,然后通过ClassLoader的loadClass ()方法动态加载一个指定的类。Java提供了以下三种途径得到ClassLoader:

·使用系统类加载器:ClassLoader.getSystemClassLoader();

·使用当前类加载器:this.getClass.getClassLoader();

·使用当前线程类加载器:Thread.currentThread().getContextClassLoader();

下面的代码片段展示了如何使用系统类加载器装载并实例化的过程:

ClassLoader cloader = ClassLoader.getSystemClassLoader();

Class cls = cloader.loadClass("com.abc.Person");

Person person = (Person)cls.newInstance();

需要注意的是:系统类加载器处理-classpath下的类加载工作,它适合于简单的命令行应用,如果是EJB, 或java Web应用,请使用其他的类加载器。

3 Java 插件的开发(Java Plugin development)

下面考虑这样一种应用场景:现有用Java语言编写的主程序,要求第三方开发人员编写扩展功能。约定第三方开发人员开发的类必须包含一个实现某已知接口的类,名称不限。要求第三方开发的类打成 jar包,与主程序分开发布。jar包放在主程序安装目录下的plugins子目录下,主程序启动时动态加载 jar 包中的类。

这是典型的Java插件技术在实际应用中的体现。为了开发java插件,java提供了URLClassLoader解决从jar文件中动态加载类。 URLClassLoader是ClassLoader的子類,它用于从指定的jar文件和目录的URL搜索路径加载类和资源,即,通过URLClassLoader就可以加载指定jar中的类到内存中[2]。下面的代码片段说明了这一过程:

// 动态加载jar包

String url_str = "file:C:\PluginDemo\plugins\WMAPlayer.jar";

URL[] urls = new URL[1];

urls[0] = new URL(url_str);

URLClassLoader loader = new URLClassLoader(urls);

//从加载器中加载插件类,然后实例出一个对象

Class<?> c = loader.loadClass("com.abc.plugin.WMAPlayer");

PlayerIface  instance = (PlayerIface) c.newInstance();

在插件开发过程中,首先由开发人员编写系统框架,并预先定义好系统的扩展接口。插件由其他开发人员(或第三方开发人员)根据预先定义好的接口编写系统的扩展功能模块。插件以独立的jar 包形式出现。对系统而言,它并不知道也不可能知道插件的具体实现,它所做的就是为插件预定义好接口。系统启动的时候根据配置文件寻找插件,并根据预定义接口将插件挂接到系统中。插件的基本结构如图1所示。

图中,MANIFEST.MF是每个插件的描述文件,应用程序加载插件时首先解析每个插件的描述文件,然后在应用程序中动态加载类并实例化,最后调用类的方法。具体而言,将每个插件打成 jar包,里面包含一个实现预定义接口的类,在jar的MANIFEST.MF文件中给出该类的全路径。在应用程序中使用URLCLassLoader装载jar包,使用java.util.jar.JarFile和java.util.jar.Manifest解析jar包和MANIFEST.MF文件[3],获得插件类以后用URLCLassLoader的loadClass()方法装载类,最后使用newInstance()创建该类的实例。

图1  插件的基本结构

下面以音乐播放器为例,具体展示Java插件的开发过程。本例将支持不同音频格式的音乐播放器做成不同的插件。

第一步:预定义系统的扩展接口。

package com.abc.demo;

public interface PlayerIface{

public void load(String filename);

public void play();

public void stop();

public String getSupportedFormat();

}

第二步:编写实现扩展接口的插件类。

下面是MP3播放器插件类,插件类实现了扩展接口PlayerIface,并且必须拥有不带参数的构造方法,以便实例化时被调用。用同样的方法可以编写其他音频格式的播放器。

package com.abc.plugin;

public class MP3Player implements PlayerIface{

//实现接口中的方法,代码略。

}

第三步:编写自己的MANIFEST.MF文件,其中有一个Plugin-Class属性,指定了插件类的完整类名。文件内容如下:

Manifest-Version: 1.0

Plugin-Class: com.abc.plugin.MP3Player

第四步:将插件类com.abc.plugin. MP3Player打成MP3Player.jar包,打包时注意将上述MANIFEST.MF文件打入包中。

第五步:将jar包发布在主程序的安装目录的子目录中,一般是plugins子目录。

如图2所示。

图2  插件的发布目录

第六步:主程序启动时在plugins子目录中列举所有插件,针对每个jar包解析其中的MANIFEST文件,提取Plugin-Class属性的值,获取插件类的完整类名。用URLCLassLoader的loadClass()方法装载该类,最后使用newInstance()创建该类的实例。

4 结束语

本文所展示的Java插件的开发方法和步骤具有一般性指导意义,可以很好地解决新功能植入或功能模块定制。作者最近在一个呼叫中心的坐席软件中就是利用Java插件技术解决了来电弹屏定制问题。正因为插件技术降低了模块之间的耦合性,便于各个模块的独立维护,方便和加快整个项目的维护更新,可以在不修改原有应用程序代码的情况下植入新的功能,或者完成某个功能模块的定制,從而在软件项目开发中得到广泛的应用。

参考文献:

[1]Java:静态和动态类加载[EB/OL].http://javaj2ee.com/node/14.

[2]动态类加载[EB/OL]. https://en.wikibooks.org/wiki/Java_Programming/

Reflection/Dynamic_Class_Loading.

[3]Java程序:从jar 包读取manifest文件[EB/OL], https://blogs.oracle.com/jmxetc/entry/a_java_program_that_reads.

作者简介:

钱宇虹(1967-),女,计算机科学硕士,副教授,研究领域:软件开发与应用、软件工程、软件测试技术。

猜你喜欢
插件
自编插件完善App Inventor与乐高机器人通信
插件技术在计算机软件技术中的运用
基于jQUerY的自定义插件开发
MapWindowGIS插件机制及应用
基于Revit MEP的插件制作探讨
插件技术在计算机软件中的应用分析