微内核插件架构从原理到实践( 三 )


 
OSGIOSGI的全称是open services gateway initiative,是一个插件化的标准,而不是一个可运行的框架 。

微内核插件架构从原理到实践

文章插图
 
OSGI的优势主要如下几点:
1.可复用性强
OSGI框架本身可复用性极强,很容易构建真正面向接口的程序架构,每一个Bundle 都是一个独立可复用的单元 。
 
2.基于OSGI的应用程序可动态更改运行状态和行为 。
在OSGI框架中,每一个Bundle实际上都是可热插拔的,因此,对一个特定的Bundle进行修改不会影响到容器中的所有应用,运行的大部分应用还是可以照常工作 。当你将修改后的Bundle再部署上去的时候,容器从来没有重新启过 。这种可动态更改状态的特性在一些及时性很强的系统中比较重要,尤其是在Java Web项目中,无需重启应用服务器就可以做到应用的更新 。
 
3.职责分离
基于OSGI框架的系统可分可合,其结构的优势性导致具体的Bundle不至于影响到全局,不会因为局部的错误导致全局系统的崩溃 。例如Java EE项目中可能会因为某个Bean的定义或注入有问题,而导致整个应用跑不起来,而使用OSGI则不会有这种问题,顶多相关的几个Bundle无法启动 。
 
OSGI同样也有一些缺点:
1.类加载机制
每个Bundle都由单独的类加载器加载,与一些Java EE项目中使用比较多的框架整合比较困难,如Spring MVC、Struts2等,例如笔者尝试在OSGI应用中整合Spring MVC时,通过DispatcherServlet启动的Bean与OSGI Bundle启动的Bean无法相互依赖,需要做特殊处理,后面文章中会有介绍 。
 
2.OSGI框架提供的管理端不够强大
现在的管理端中仅提供了基本的Bundle状态管理、日志查看等功能,像动态修改系统级别的配置(config.ini)、动态修改Bundle的配置(Manifest.mf)、启动级别等功能都尚未提供,而这些在实际的项目或产品中都是非常有必要的 。
 
3.使用成本高
采用OSGI作为规范的模块开发、部署方式自然给现有开发人员提出了新的要求,需要学习新的基于OSGI的开发方式 。
微内核插件架构设计1.定义扩展点注解@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface Extension {String tenantId() default "0";//根据SaaS租户ID进行隔离int ordinal() default 0;Class<? extends ExtensionPoint>[] points() default {};String[] plugins() default {};}2.定义业务扩展点扩展点表示一块逻辑在不同的业务有不同的实现,使用扩展点做接口申明 。
public interface ExtensionPoint {}3.插件实体定义主程序定义插件类Plugin,用于封装从插件配置文件读取业务扩展实现,具体定义如下:
@Datapublic class Plugin {/*** 插件名称*/private String pluginId;/*** 插件版本*/private String version;/*** 插件路径*/private String path;/*** 插件类全路径*/private String className;}4.插件管理创建插件管理类,初始化插件 。
/** * 使用URLClassLoader动态加载jar文件,实例化插件中的对象 * */public abstract class PluginManager {private Map<String, Class> clazzMap = new HashMap<>();public PluginManager(List<Plugin> plugins) throws PluginException {init(plugins);}/*** 插件初始化方法*/private void init(List<Plugin> plugins) throws MalformedURLException {try{int size = plugins.size();for(int i = 0; i < size; i++) {Plugin plugin = plugins.get(i);String filePath = plugin.getPath();// URL url = new URL("file:" + filePath);URL url = new File(plugin.getPath()).toURI().toURL();URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});Class<?> clazz = urlClassLoader.loadClass(plugin.getClassName());clazzMap.put(plugin.getClassName(), clazz);}}catch (Exception e) {throw new PluginException("plugin " + plugin.getPluginName() + " init error," + e.getMessage());}}/*** 获得插件* @param className 插件类全路径* @return* @throws PluginException*/public ExtensionPoint getInstance(String className) throws PluginException {// 插件实例化对象,插件都是实现ExtensionPoint接口Class clazz = clazzMap.get(className);Object instance = null;try {instance = clazz.newInstance();} catch (Exception e) {throw new PluginException("plugin " + className + " instantiate error," + e.getMessage());}return (ExtensionPoint)instance;}PluginState startPlugin(String pluginId);/*** 停止所有插件*/void stopPlugins();/*** 停止插件*/PluginState stopPlugin(String pluginId);/*** 卸载所有插件*/void unloadPlugins();/***卸载插件*/boolean unloadPlugin(String pluginId);}5.测试public class Main {public static void main(String[] args) {try {// 从配置文件加载插件List<Plugin> pluginList = PluginLoader.load();// 初始化插件管理类PluginManager pluginManager = new PluginManager(pluginList);// 循环调用所有插件for(Plugin plugin : pluginList) {ExtensionPoint extensionPoint = pluginManager.getInstance(plugin.getClassName());System.out.println("开始执行[" + plugin.getName() + "]插件...");// 调用插件extensionPoint.xxx();System.out.println("[" + plugin.getName() + "]插件执行完成");}} catch (Exception e) {e.printStackTrace();}}}


推荐阅读