插件框架Java

 

HOW TO MAKE PLUGIN FRAMEWORK

       本文将为你展示如何基于Java[1]构建一个完整可用的插件框架(Plugin Framework)。
关于Plugin Wikipedia[2]是这样描述的

Wikipedia 写道

插件(Plug-in,又稱addin, add-in, addon 或 add-on),简单说,就是电脑程序,通过和应用程序(例如网页浏览器,电子邮件服务器)的互动,提供一些所需要的特定的功能。

 

        我们可以简单的理解插件就是应用程序的一个模块,但是这个模块又是相对独立的自治域,这个模块可以引用(依赖)其他的模块,这个模块可以通过某种方式于其他的模块交互。应用程序不依赖任何插件就可以运行,但是需要加载相应的插件才能实现特定的功能。

        在了解了插件的基本知识后,我们分析一下构建一个插件框架需要处理那些问题

 

 

1:定义
    1.1:物理布局
    1.2:逻辑实体
2:插件管理器
    2.1:依赖关系
    2.2:库隔离
    2.3:生命周期管理
    2.4:插件之间的通讯
 

 1.定义

 

我们希望插件保持内聚,这样能方便插件的安装和卸载,在逻辑实体上希望插件的定义尽可能的简单,下面我们给出插件的

物理存储布局和逻辑实体
1.1物理布局
    所谓物理布局就是插件是如何存储的,下图给出了一个简单的插件布局

 

samplePlugin/
|-- config
| `-- default.xml
|-- libs
| |-- liba.jar
| `-- libb.so
| |-- sampleplugin.jar
`-- plugin.xml

 插件以文件夹为单位,每个文件夹是单独的插件,每个插件文件夹都应该包含libs目录和config目录这两个目录分别存储了
插件需要的库文件和配置文件,libs目录下的文件会在插件加载的过程中自动加载,还有就是最重要的插件描述文件plugin.xml。一个简单的插件描述文件如下所示

 

 

Xml代码

  1. <? xml  version = "1.0"   encoding ="utf-8" ?>   
  2. < plugin  id = "cn.edu.nwpu.as.VmasPlugin"  name = "vmas"   version ="1.0.0"   author = "liuqiang" >    
  3.     < runtime   path = "cn.edu.nwpu.as.VmasPlugin.jar" />   
  4.     < require   id = "org.opensolaris.gear.processtree.ProcessTreePlugin"   version = "1.0.1" />   
  5. </ plugin >   
<?xml version="1.0" encoding="utf-8"?>
<plugin id="cn.edu.nwpu.as.VmasPlugin" name="vmas" version="1.0.0" author="liuqiang"> 
	<runtime path="cn.edu.nwpu.as.VmasPlugin.jar"/>
	<require id="org.opensolaris.gear.processtree.ProcessTreePlugin" version="1.0.1"/>
</plugin>

 插件应该通过插件描述文件plugin.xml描述自身的一些基本属性,包括 插件的id,这个id应该是全局唯一的。插件的名字,版本
,作者等等。除此之外插件还应该描述自身的运行时环境,依赖的插件。
    关于运行时环境
    插件应该将自己所需的库文件和class文件都放到libs目录下,这些文件将被加载到插件的classpath中。以来关系也非常重
要,在插件加载的过程中,插件管理器会根据plugin.xml中描述的依赖关系选择插件加载的次序。不正确的插件依赖关系会导致
加载失败。

 

1.2逻辑实体
    一个简单的接口定义了插件的逻辑实体

 

Java代码

  1. public   interface  Plugin {  
  2.     public   void  init();  
  3.     public   void  start();  
  4.     public   void  destory();  
  5.     public  IExtension getExtension(String id);  
  6. }  
public interface Plugin {
	public void init();
	public void start();
	public void destory();
	public IExtension getExtension(String id);
}

 所有实现了这个接口的类都可以认为是一个插件,其中init方法在插件初始化的时候调用并且只调用一次,start方法在插件启
动的时候调用,destory在插件销毁的时候调用。为了确保重复加载,start方法和destory方法应该保证对称。

 

2 插件管理器
    插件管理器负责管理和插件有关的一切,在系统启动的时候,插件管理期扫描插件目录,找到符合定义的插件目录,验证合
法性,读取插件描述文件,扫描库文件。然后根据插件描述文件构建插件依赖关系图,确定插件启动顺序,然后按照顺序启动插
件,设定安全管理器,设定classloader,执行init方法,执行start方法。
以上就是插件管理器加载插件的全过程,在详细讲解每一步之前我们先了解下Java的classloader。

 

以下是JDK中关于ClassLoader的解释[3]

 

类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生
成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由
Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没
有类加载器。
应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。
类加载器通常由安全管理器使用,用于指示安全域。
ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时
,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载
器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

 

下面这张图说明了Java类加载器的结构

 

写道

+-----------------------------+
|          Bootstrap             |
|                 |                      |
|           System                |
|                 |                      |
|         Application          |
|                / /                     |
|      PluginFramework  |
|                     / /                |
|          Plugin1 Plugin2 |
+-----------------------------+

 

 上图很好的解释了Java程序的类加载过程,首先是Bootstrap加载jre的类,然后时System加载CLASSPATH下的类,然后是应用
 程序的类加载器(如果有的话)加载属于应用程序自己的类,接着PluginFrameWork加载插件管理器所需类,从图中我们可以看
 到插件管理器加载的类被所有的插件共享,但是每个插件都有自己的命名空间,这很好的解决了插件之间的库隔离问题,两个
 不同的插件现在可以加载同一个类的两个版本而不会有任何冲突。

 

为了实现上面的类加载方式,我们简单的扩展URLClassLoader实现了简单的PluginClassLoader

Java代码

  1. public   class  PluginClassLoader  extends URLClassLoader {  
  2.     private  PluginImage image;  
  3.     private  ClassLoader[] requireClassLoader;  
  4.     public  PluginClassLoader(URL[] urls) {  
  5.         super (urls);  
  6.     }  
  7.     public  PluginClassLoader(PluginImage image,ClassLoader parent) {  
  8.         super (image.getUrls(),parent);  
  9.         this .image = image;  
  10.         image.pluginClassLoader = this ;  
  11.         if  (image.getRequires() == null ) {  
  12.             requireClassLoader = null ;  
  13.   
  14.         } else  {  
  15.             requireClassLoader = new  ClassLoader[image.getRequires().size()];  
  16.             for ( int  i= 0 ;i<image.getRequires().size();i++){  
  17.                 requireClassLoader[i] = PluginManager.getInstance()  
  18.             .getPluginImage(image.getRequires().get(i).getId()).getPluginClassLoader();  
  19.             }  
  20.         }  
  21.     }  
  22.     protected  Class<?> findClass(String name) throws  ClassNotFoundException {  
  23.         Class<?> c = null ;  
  24.         try  {  
  25.             c = super .findClass(name);  
  26.         } catch  (ClassNotFoundException e) {  
  27.             if  (requireClassLoader == null )  
  28.                 throw   new  ClassNotFoundException();  
  29.             else  {  
  30.                 for  (ClassLoader l : requireClassLoader) {  
  31.                     try  {  
  32.                         if  (l != null )  
  33.                             c = l.loadClass(name);  
  34.                     } catch  (ClassNotFoundException ee) {}  
  35.                       
  36.                 }  
  37.                 if  (c ==  null ) {  
  38.                     throw   new  ClassNotFoundException();  
  39.                 }  
  40.             }  
  41.         }  
  42.         return  c;  
  43.     }  
  44. protected  String findLibrary(String name) {  
  45.         Hashtable<String, String> libs = image.getLibrarys();  
  46.         if  (libs.containsKey(name)) {  
  47.             return  libs.get(name);  
  48.             // full name   
  49.         } else   if  (libs.containsKey( "lib"  + name +  ".so" )) {  
  50.             return  libs.get("lib"  + name +  ".so");  
  51.         } else  {  
  52.             return   null ;  
  53.         }  
  54.   
  55.     }  
  56.     public  PluginImage getImage() {  
  57.         return  image;  
  58.     }  
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值