JModuleLink使用文档

/ JModuleLink / 没有评论 / 24浏览

第一部分 简介

JModuleLink是一个基于JAVA的模块化开发框架,它提供在运行时动态加载模块(一个或一组JAR包)、卸载模块的API,使开发者更加关注业务本身。

1.1 需求背景

现在在公司主要从事支付平台的开发工作,随着业务的增长,对接的银行支付通道越来越多,用户也不满足在一个通道下面开通单个商户,存在一个银行通道按照不同商户进行支付的需求,在传统实现当中,每次涉及到银行通道或者商户号的变更都需要重启服务器,体验较差。单个通道出现异常会影响其他通道的正常使用,用户对商户号的修改需要依赖开发人员对系统进行配置,增大后期维护成本。在同一个支付通道的多商户的背景下,部门银行提供的开发包无法兼容,需要对其进行隔离,于是在系统中使用了自定义的ClassLoader突破默认的双亲委托,自定义加载所需三方包以解决 冲突的问题,偶然的机会看到了阿里开源的jarslink,所以借鉴了其部分实现方式,编写了JModuleLink

传统开发模式问题:

1.2 模块化开发的好处

1.3 特性

1.3.1 隔离性

1.3.2 动态性

1.3.3 易用性

第二部分 开始使用

使用JModuleLink可以直接下载源代码编译或者下载已经编译的jar文件,如果您是使用maven来构建项目,也可以直接在pom.xml中添加JModuleLink的坐标:

Maven central

<!-- http://mvnrepository.com/artifact/com.jianggujin/JModuleLink -->
<dependency>
    <groupId>com.jianggujin</groupId>
    <artifactId>JModuleLink</artifactId>
    <version>最新版本</version>
</dependency>

最新的版本可以从Maven仓库或者码云获取。

2.1 编写模块

对于模块而言,简单的来说就是Action的合集,所以我们需要掌握如何开发Action,编写一个Action我们只需要创建一个普通的类,然后让其实现JAction接口即可。

package com.jianggujin.modulelink.test;

import com.jianggujin.modulelink.JAction;
import com.jianggujin.modulelink.JModuleLinkException;

public class CustomAction implements JAction {

   @Override
   public Object execute(Object in) throws JModuleLinkException {
      // 真正的业务逻辑
      return null;
   }

   @Override
   public String getActionName() {
      return "custom";
   }

   @Override
   public boolean isDefault(String moduleName) {
      return false;
   }
}

JAction中包含三个方法:

修饰符与类型方法描述
Objectexecute(Object in)真正的业务逻辑处理方法
StringgetActionName()Action的名称,在同一个模块中,该值唯一
booleanisDefault(String moduleName)该方法用于判断Action在模块中
是否为默认的Action
用于找不到指定Action的时候
的默认处理(人为控制是否使用)

最后只需要将编写好的模块打包即可。建议一个模块就是一个FAT JAR,当然了,这并不是强制的,JModuleLink也支持一个模块有多个资源或jar

2.2 初始化 JModuleManager

模块的加载运行需要JModuleLink的核心支持,首先我们需要初始化JModuleManager,使用该类可以完成模块的加载、卸载、查找等操作,建议使用单例模式初始化该类的对象,使用全局唯一的模块管理器管理模块。

JModuleManager moduleManager = new JModuleManagerImpl();

除此之外,还可以使用更加简单的方式来获得JModuleManager的实例对象:

JModuleManager moduleManager = JModuleManagerImpl.getInstance();

该种方式获得的JModuleManager对象为单例对象,且只有第一次调用该方法的时候才会真正初始化。

2.3 加载模块

我们可以使用JModuleConfig来定义一个模块信息。配置说明如下:

名称说明
name模块名,建议用英文命名,全局唯一
desc模块描述
version模块版本
moduleUrls模块的资源地址,比如jar文件的位置
actions需要使用的action,值为全限定类名
exclusionActions需要排除的action,值为全限定类名
scanPackages需要扫描的包,不为空时会启动扫描。默认的实现会扫描包下面的JAction实现类;如果使用spring, 将自动扫描注解形式的bean ,并装配扫描到的JAction
active是否激活,仅当模块有多个版本的时候有效

使用示例:

JModuleConfig moduleConfig = new JModuleConfig("moduleName", "moduleVersion");
moduleConfig.setDesc(moduleDesc);
moduleConfig.addModuleUrl(new File(modulePath).toURI().toURL());
moduleConfig.addScanPackage(scanPackage);
moduleManager.load(moduleConfig);

这样就完成了一个模块的加载。

2.4 使用模块

模块正常加载后,我们就可以通过模块管理器查找已经加载的模块并执行指定的Action。一种比较推荐的使用方式如下:

public Object service(String moduleName, String actionName) {
   if (moduleManager.has(moduleName)) {
      JModule module = moduleManager.find(moduleName);
      if (actionName == null) {
         if (module.hasDefaultAction()) {
            return module.doDefaultAction(null);
         }
      } else {
         if (module.hasAction(actionName)) {
            return module.doAction(actionName, null);
         }
      }
      throw new JModuleNotFoundException("Not found action.");
   }
   throw new JModuleNotFoundException("Not found module.");
}

建议直接使用JModule提供的doAction方法执行指定的Action,或者使用JModuleUtils提供的doActionWithinModuleClassLoader方法执行(这两者本质相同),否则可能会导致模块的类加载器无法正常使用。

Action的参数按照实际情况传递

2.5 AB测试

如果我们希望对一个模块的不同版本进行AB TEST,我们只需要加载指定模块的不同版本,然后使用模块管理器的activeVersion(String name, String version)可以直接切换模块默认使用的版本。

2.6 模块卸载销毁

当一个模块或模块的版本不再使用的时候,我们可以动态的将其卸载,JModuleManager提供了相应的卸载方法:

   /**
    * 卸载一个模块
    */
   void unload(String name);

   /**
    * 卸载一个模块的指定版本
    */
   void unload(String name, String version);

需要注意的是如果仅仅是卸载模块的指定版本,且该版本为当前正在使用的版本,那么被卸载的模块中除去卸载版本后不能含有多个版本,否则无法卸载。

卸载模块的时候必须调用JModuleManagerunload方法实现,虽然该接口提供了查找JModule的方法,并且JModule存在destroy()方法,但是我们依然不建议直接调用模块的的销毁方法,避免管理器模块状态不一致出现错误。

2.7 spring

JModuleLink除了默认的模块管理器实现,也针对spring提供了spring的模块管理器实现。使用spring的模块管理器,我们仅需要将JModuleManagerImpl换成JSpringModuleManager,一般情况下,我们会将该对象实例交给spring管理。

另外,与默认的通用实现不同的地方还有,在加载模块的时候,建议使用JSpringModuleConfig作为模块信息的配置类。JSpringModuleConfigJModuleConfig的基础上增加了3个配置项。配置说明如下:

名称说明
properties模块里的bean需要的配置信息,集成了spring properties
xmlPatterns需要加载的 XML配置
exclusions需要排除的配置文件

需要注意的是,如果配置中我们添加了扫描的包,在初始化spring模块的上下文的时候将使用注解的形式扫描,否则使用XML方式。

2.8 Web环境

考虑到有些时候可能会将JModuleLink用于Web环境。为了方便操作,提供了Web专用的模块管理器,子模块可以共享程序上下文属性(只读),使用Web环境,我们需要把管理器换成JWebModuleManager,在编写Action的时候,我们可以直接继承JServletActionJHttpServletAction,这两个实现本质上都是Servlet,这样我们就可以像编写普通Servlet代码一样编写模块的Action。管理器会在模块初始化和销毁的时候回调其对应的方法(与传统Servlet保持一致)。

需要特殊注意的是使用JWebModuleManager加载的模块为JWebModule,在原有模块的基础上提供了doServletActiondoDefaultAction方法,可以直接传递请求和响应对象。

Web环境中对应的配置类为JWebModuleConfig,在默认的JModuleConfig基础上新增了如下参数:

名称说明
initParameters上下文参数
servletInitParametersServlet参数,键值为Action的名字
webRootPathWeb根目录,会在该目录下按照标准war解压后的文件结构添加类路径

第三部分 日志

因为JModuleLink的大部分常用功能不需要依赖任何三方包,所以提供了一种轻量的日志方式,默认会在控制台输出日志内容,我们可以按照实际集成的日志三方包,编写日志的实现类,然后调用JLogFactorysetImplementation方法设置日志实现类,该类需要实现JLog接口。

从1.0.2版本开始默认集成了JLogging,如果不引用该包,用法与原来相同。引用后日志将会使用Jlogging实现,原有代码无需修改。