插件(Plugin)是什么不用多说,用过Eclipse就知道Eclipse有很多插件。但本文的内容不是Eclipse插件开发。
插件是根据软件提供的接口编写出来的程序,很多软件都支持插件,例如Eclipse、Photoshop、VisualStudio。插件可以动态给软件添加一些功能,也可以随时删除,这样的好处是任何人都可以给这个软件进行功能上的扩展,而不用去改软件本身的代码。
一、适用场景
比如需要开发一个系统,用来将一些有数据推送给客户,至于是什么数据不是重点。有三个客户:A客户需要把数据组织成一个xml格式的文件,通过FTP上传到客户服务器上;B客户需要把数据组织成一个json,通过HTTP请求提交;C客户希望生成一个Excel文件再通过E-mail发送...以后可能还会有更多的客户,也还会有更多操蛋的需求。
对于这样一个系统的开发,如果使用普通的方式开发,那么每增加一个客户就要修改一次系统代码,在代码中增加一个针对某个客户的功能,很不灵活。如果再减少一个客户,那么其对应的代码也就没有用了,是不是要删除掉又成了问题。
以上只是一个例子,在实际开发中经常会有类似的情形,此时使用插件化的方式会更灵活。
可以把数据的获取和整理这块和客户无关的逻辑放在主程序中,而主程序提供一个客户推送的接口,接口定义一个未实现的抽象方法“推送数据”,这个方法由各个客户对应的插件来实现。这样新增一个客户需求,不需要修改主程序的代码,只需要实现这个接口就行,插件写好打成jar包放在指定目录下,再配置一下,主程序就可以使用这个插件了。当不需要这个插件,也可以通过配置来去掉它。
二、主程序配置插件
上面说到主程序可以通过配置来动态添加和删除插件,配置的方式一般有两种:XML或数据库,二者选其一即可。
方法1:XML
主程序可以通过一个xml配置文件,动态配置插件。
<?xml version="1.0" encoding="UTF-8"?>
<plugins>
<plugin>
<name>A客户插件</name>
<jar>D:/plugin/a-plugin.jar</jar>
<class>com.xxg.aplugin.APlugin</class>
</plugin>
<plugin>
<name>B客户插件</name>
<jar>D:/plugin/b-plugin.jar</jar>
<class>com.xxg.bplugin.BPlugin</class>
</plugin>
<plugin>
<name>C客户插件</name>
<jar>D:/plugin/c-plugin.jar</jar>
<class>com.xxg.cplugin.CPlugin</class>
</plugin>
</plugins>
主程序通过解析这个XML来调用插件,<plugin>元素即一个插件,可以通过添加和删除<plugin>元素来动态的添加和删除插件。<name>是插件名称,<jar>是插件jar文件所在的路径,<class>是插件实现主程序接口的类。
方法2:数据库
如果使用数据库来配置插件,需要一个插件表:
插件表(plugin_info):
id
|
int
|
主键
|
name
|
varchar
|
插件名称
|
jar
|
varchar
|
插件jar文件路径
|
class
|
varchar
|
实现主程序接口的类的包名加类名
|
两种方法的区别:
两种方式从功能上来说是一样的。使用数据库方式的好处是可以很方遍的再开发一个管理界面来管理,不好的地方就是依赖数据库。我更推荐XML的方式,不依赖数据库。
三、主程序
下面是以XML作为插件配置方式的调用插件的主程序。
主程序需要提供一个接口来提供给插件开发者来实现:
package com.xxg.main;
public interface PluginService
{
public void service();
}
上面是一个接口,包含一个未实现的方法service(),这个方法即和客户相关的逻辑,由插件来实现。
插件封装类:
package com.xxg.main;
public class Plugin
{
private String name;
private String jar;
private String className;
// setter、getter省略…
}
解析XML获取所有插件信息(这里用到DOM4J):
package com.xxg.main;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class XMLParser
{
public static List<Plugin> getPluginList() throws DocumentException
{
List<Plugin> list = new ArrayList<Plugin>();
SAXReader saxReader =new SAXReader();
Document document = saxReader.read(new File("plugin.xml"));
Element root = document.getRootElement();
List<?> plugins = root.elements("plugin");
for(Object pluginObj : plugins)
{
Element pluginEle = (Element)pluginObj;
Plugin plugin = new Plugin();
plugin.setName(pluginEle.elementText("name"));
plugin.setJar(pluginEle.elementText("jar"));
plugin.setClassName(pluginEle.elementText("class"));
list.add(plugin);
}
return list;
}
}
使用URLClassLoader动态加载jar文件,实例化插件中的对象:
package com.xxg.main;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
public class PluginManager
{
private URLClassLoader urlClassLoader;
public PluginManager(List<Plugin> plugins) throws MalformedURLException
{
init(plugins);
}
private void init(List<Plugin> plugins) throws MalformedURLException
{
int size = plugins.size();
URL[] urls = new URL[size];
for(int i = 0; i < size; i++)
{
Plugin plugin = plugins.get(i);
String filePath = plugin.getJar();
urls[i] = new URL("file:" + filePath);
}
// 将jar文件组成数组,来创建一个URLClassLoader
urlClassLoader = new URLClassLoader(urls);
}
public PluginService getInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
// 插件实例化对象,插件都是实现PluginService接口
Class<?> clazz = urlClassLoader.loadClass(className);
Object instance = clazz.newInstance();
return (PluginService)instance;
}
}
main函数依次调用插件逻辑:
package com.xxg.main;
import java.util.List;
public class Main
{
public static void main(String[] args)
{
try
{
List<Plugin> pluginList = XMLParser.getPluginList();
PluginManager pluginManager = new PluginManager(pluginList);
for(Plugin plugin : pluginList)
{
PluginService pluginService = pluginManager.getInstance(plugin.getClassName());
System.out.println("开始执行[" + plugin.getName() + "]插件...");
// 调用插件
pluginService.service();
System.out.println("[" + plugin.getName() + "]插件执行完成");
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
四、插件开发
插件开发很简单,只需要把主程序的jar包引入到项目中,再实现主程序提供的接口就行:
package com.xxg.aplugin;
import com.xxg.main.PluginService;
public class APlugin implements PluginService
{
@Override
public void service()
{
System.out.println("A客户插件正在执行~");
}
}
service()方法应该实现客户相关的逻辑,即实现插件的功能。这里就用一句System.out.println来代替。
插件实现完成后,打个jar包,注意不要把主程序的部分也打到jar里。
再实现其他插件,插件实现完成后,配置主程序的plugin.xml。
五、执行结果
配置好plugin.xml,插件jar放到配置的路径下:
运行主程序main方法:
开始执行[A客户插件]插件...
A客户插件正在执行~
[A客户插件]插件执行完成
开始执行[B客户插件]插件...
B客户插件正在执行~
[B客户插件]插件执行完成
开始执行[C客户插件]插件...
C客户插件正在执行~
[C客户插件]插件执行完成
六、service()参数、返回值
如果逻辑需要的话,service()可以添加参数和返回值。例如主程序需要传入数据给插件,可以加入参数,插件需要返回结果给主程序,可以加入返回值。
例如传给插件一些插件需要的配置项。在上面的场景中,各个客户的需求不同。A需要FTP上传,那么需要FTP服务器的地址、端口号、用户名、密码配置项;B需要HTTP请求,那么需要请求地址配置项;C需要发送邮件,那么需要e-mail地址配置项。
这些配置项可以统一配置在XML或数据库中。
XML:
每个插件元素中加入<properties>元素来配置。
<?xml version="1.0" encoding="UTF-8"?>
<plugins>
<plugin>
<name>A客户插件</name>
<jar>D:/plugin/a-plugin.jar</jar>
<class>com.xxg.aplugin.APlugin</class>
<properties>
<property name="FTP_IP">192.168.7.1</property>
<property name="FTP_PORT">21</property>
<property name="FTP_USERNAME">XXG</property>
<property name="FTP_PASSWORD">123456</property>
</properties>
</plugin>
<plugin>
<name>B客户插件</name>
<jar>D:/plugin/b-plugin.jar</jar>
<class>com.xxg.bplugin.BPlugin</class>
<properties>
<property name="URL">http://www.xxg.com/api</property>
</properties>
</plugin>
<plugin>
<name>C客户插件</name>
<jar>D:/plugin/c-plugin.jar</jar>
<class>com.xxg.cplugin.CPlugin</class>
<properties>
<property name="EMAIL">xxg@xxg.com</property>
</properties>
</plugin>
</plugins>
数据库:
再加一个插件配置表(plugin_config_info):
id
|
int
|
主键
|
plugin_id
|
int
|
外键,关联插件表id
|
key
|
varchar
|
配置项键
|
value
|
varchar
|
配置项值
|
主程序定义接口,加入一个Map<String,String>参数来传入这些配置:
package com.xxg.main;
import java.util.Map;
public interface PluginService
{
public void service(Map<String,String> configs);
}
在插件中,可以获取这些配置:
package com.xxg.aplugin;
import java.util.Map;
import com.xxg.main.PluginService;
public class APlugin implements PluginService
{
@Override
public void service(Map<String, String> configs)
{
String ftpIp = configs.get("FTP_IP");
String ftpPort = configs.get("FTP_PORT");
String ftpUsername = configs.get("FTP_USERNAME");
String ftpPassword = configs.get("FTP_PASSWORD");
// ...
}
}
插件获取的配置项首先应该判断是否为null,防止忘了添加一些配置项。
作者:叉叉哥 转载请注明出处:http://blog.csdn.net/xiao__gui/article/details/9239743
分享到:
相关推荐
该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。如果不是以该字符结束,则认为该 URL 指向一个将根据需要打开的 JAR 文件pac
执行链实际为:URLClassLoader.class.getConstructor(java.net.URL[].class).newInstance(new
让Java支持热加载是个不错的想法。如何做到的呢? 1. 定义好接口和实现类 2. 让代理类通过反射的方式调用实现类,对外暴露的...Java实现热加载; Java动态加载class; Java覆盖已加载的class; Java自定义classloader;
传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java' * 7.创建任务并执行 * 8.获取执行完成后的返回JavaClassObject类 * 9.创建DynamicClassLoader来...
NULL 博文链接:https://shihuan830619.iteye.com/blog/2160884
at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang....
3.9.4 java.net.URLClassLoader 49 3.9.5 类的路径 50 3.10 java.lang.SecurityManager 51 3.10.1 使用安全管理器的实例 51 3.10.2 JDK1.2中没有改变的API 52 3.10.3 JDK1.2中禁用的方法 53 3.11 java.security....
3.9.4 java.net.URLClassLoader 49 3.9.5 类的路径 50 3.10 java.lang.SecurityManager 51 3.10.1 使用安全管理器的实例 51 3.10.2 JDK1.2中没有改变的API 52 3.10.3 JDK1.2中禁用的方法 53 3.11 java.security....
at java.net.URLClassLoader.findClass(URLClassLoader.java:434) at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:204) at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:688...
Java Platform Standard Edition 7 Documentation What's New Documentation Release Notes Tutorials and Training The Java Tutorials Java Training More Information Java SE 7 Names and ...
使用URLClassLoader加载本地文件夹c盘下的test.jar文件
NULL 博文链接:https://yklovejava-163-com.iteye.com/blog/1736631
1.4 开发Java的准备 7 1.4.1 安装JDK 8 学生提问:不是说JVM是运行Java程序的虚拟机吗?那JRE和JVM的关系是怎样的呢? 8 学生提问:为什么不安装公共JRE系统呢? 9 1.4.2 设置PATH环境变量 10 学生提问:为什么...
at java.net.URLClassLoader.defineClass(Unknown Source) at java.net.URLClassLoader.access$100(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController....
尝试使用资源声明禁止的例外赶上许多例外带有类型检查的反复例外轻松进行反射的例外处理对象类别和空值检查URLCLASSLOADER中的关闭方法文件和目录的增强手表服务二元文学开关声明中的字符串类型推论/钻石算子在数字...
Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class文件,动态实现spring bean 的动态加载,并通过UrlClassLoader完成jar和class文件的加载。可以实现jar的热替换。spring的bean动态加载则需要对...
at java.net.URLClassLoader$1.run(URLClassLoader.java:202) at java.security.AccessController.doPrivileged(NativeMethod) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at sun.misc....
7.10 继承Java类及实现Java接口 157 7.11 异常 158 7.12 Shell脚本 159 7.12.1 执行Shell命令 159 7.12.2 字符串插值 160 7.12.3 脚本输入 161 7.13 Nashorn和JavaFX 162 练习 164 第8章杂项改进 166 8.1 字符串 168...
C_C_和Java安全编码实践提示与技巧,C_C_和Java安全编码实践提示与技巧,C_C_和Java安全编码实践提示与技巧
2):网络加载:java.net.URLClassLoader可以加载url指定的类 3):从jar、zip等等压缩文件加载类,自动解析jar文件找到class文件去加载util类 4):从java源代码文件动态编译成为class文件 当一个类被加载、连接、...