JModuleLink
是一个基于JAVA
的模块化开发框架,它提供在运行时动态加载模块(一个或一组JAR包)、卸载模块的API,使开发者更加关注业务本身。
现在在公司主要从事支付平台的开发工作,随着业务的增长,对接的银行支付通道越来越多,用户也不满足在一个通道下面开通单个商户,存在一个银行通道按照不同商户进行支付的需求,在传统实现当中,每次涉及到银行通道或者商户号的变更都需要重启服务器,体验较差。单个通道出现异常会影响其他通道的正常使用,用户对商户号的修改需要依赖开发人员对系统进行配置,增大后期维护成本。在同一个支付通道的多商户的背景下,部门银行提供的开发包无法兼容,需要对其进行隔离,于是在系统中使用了自定义的ClassLoader
突破默认的双亲委托,自定义加载所需三方包以解决 冲突的问题,偶然的机会看到了阿里开源的jarslink
,所以借鉴了其部分实现方式,编写了JModuleLink
。
传统开发模式问题:
Class
使用单独的ClassLoader
来加载,每个模块可以依赖同一种框架的不同的版本。Spring
上下文,来加载模块中的BEAN,实例化失败不会影响其他模块(Spring环境)。使用JModuleLink
可以直接下载源代码编译或者下载已经编译的jar
文件,如果您是使用maven
来构建项目,也可以直接在pom.xml
中添加JModuleLink
的坐标:
<!-- http://mvnrepository.com/artifact/com.jianggujin/JModuleLink -->
<dependency>
<groupId>com.jianggujin</groupId>
<artifactId>JModuleLink</artifactId>
<version>最新版本</version>
</dependency>
对于模块而言,简单的来说就是Action
的合集,所以我们需要掌握如何开发Action
,编写一个Action
我们只需要创建一个普通的类,然后让其实现JAction
接口即可。
package com.jianggujin.modulelink.test.module;
import com.jianggujin.modulelink.JAction;
import com.jianggujin.modulelink.exception.JModuleLinkException;
/**
* 自定义Action
*
* @author jianggujin
*
*/
public class CustomAction implements JAction {
@Override
public Object execute(JModuleContext context) throws JModuleLinkException {
// TODO
return null;
}
@Override
public String getActionName() {
return "customAction";
}
@Override
public boolean isDefault(String moduleName) {
return false;
}
}
在JAction
中包含三个方法:
修饰符与类型 | 方法 | 描述 |
---|---|---|
Object | execute(JModuleContext context) | 真正的业务逻辑处理方法 |
String | getActionName() |
Action 的名称,在同一个模块中,该值唯一 |
boolean | isDefault(String moduleName) | 该方法用于判断Action 在模块中是否为默认的 Action ,用于找不到指定 Action 的时候的默认处理(可控制是否使用) |
最后只需要将编写好的模块打包即可。建议一个模块就是一个FAT JAR
,当然了,这并不是强制的,JModuleLink
也支持一个模块有多个资源或jar
。除了上面的写法之外,JModuleLink
提供了一个JAction
的抽象实现JAbstractAction
,多数情况下,我们只需要重写execute
方法即可,默认的Action
名称为当前类名首字母小写。
package com.jianggujin.modulelink.test.module;
import com.jianggujin.modulelink.exception.JModuleLinkException;
import com.jianggujin.modulelink.support.JAbstractAction;
/**
* 自定义Action
*
* @author jianggujin
*
*/
public class CustomAction extends JAbstractAction {
@Override
public Object execute(JModuleContext context) throws JModuleLinkException {
// TODO
return null;
}
}
模块的加载运行需要JModuleLink
的核心支持,首先我们需要初始化JModuleManager
,使用该类可以完成模块的加载、卸载、查找等操作,建议使用单例模式初始化该类的对象,使用全局唯一的模块管理器管理模块。
JModuleManager moduleManager = new JDefaultModuleManager();
除此之外,还可以使用更加简单的方式来获得JModuleManager
的实例对象:
JModuleManager moduleManager = JDefaultModuleManager.getInstance();
该种方式获得的JModuleManager
对象为单例对象,且只有第一次调用该方法的时候才会真正初始化。
我们可以使用JModuleConfig
来定义一个模块信息。配置说明如下:
名称 | 说明 |
---|---|
moduleName | 模块名,建议用英文命名,全局唯一 |
description | 模块描述 |
moduleUrls | 模块的资源地址,比如jar文件的位置 |
exclusionActions | 需要排除的action,值为全限定类名 |
scanPackages | 需要扫描的包,不为空时会启动扫描包下面的Action 、监听器、拦截器。如果使用Spring配置且模块环境为Spring,该值为空时,尝试使用XML加载,否则使用注解。 在SpringBoot模块环境下该值无效 |
reload | 重新加载,如果该值为true ,加载模块时会先卸载已加载模块然后重新加载 |
exclusionModuleListeners | 需要排除的模块事件监听器 |
exclusionInterceptors | 需要排除的拦截器 |
使用示例:
JModuleConfig moduleConfig = new JModuleConfig(moduleName);
moduleConfig.setDescription(moduleDesc);
moduleConfig.addModuleUrl(moduleUrl);
moduleConfig.addScanPackage(scanPackage);
moduleManager.load(moduleConfig);
这样就完成了一个模块的加载。
模块正常加载后,我们就可以通过模块管理器查找已经加载的模块并执行指定的Action
。使用方式如下:
public Object service(String moduleName, String actionName) {
JModule module = moduleManager.getModule(moduleName);
if (actionName == null) {
return module.doDefaultAction(null);
}
return module.doAction(actionName, null);
}
Action
的参数按照实际情况传递
当一个模块不再使用的时候,我们可以动态的将其卸载,JModuleManager
提供了相应的卸载方法:
/**
* 卸载一个模块
*/
void unload(String moduleName);
卸载模块的时候必须调用
JModuleManager
的unload
方法实现,虽然该接口提供了查找JModule
的方法,并且JModule
存在destroy()
方法,但是我们依然不建议直接调用模块的的销毁方法,避免管理器模块状态不一致出现错误。
具体示例请参考:https://gitee.com/jianggujin/JModuleLink-demo
在JModuleLink
中,我们可以对模块的开始加载、加载完成、激活、取消激活、卸载事件进行监听,以便于我们在代码中做一些额外的处理,监听模块的事件,我们需要实现JModuleListener
接口,模块加载过程中,会先尝试从模块中加载实现了该接口的监听类,然后才会加载相关的Action
,以便于监听器可以完整的获得事件回调。如果只需要监听模块的部分事件,JModuleLink
中还提供了一个JModuleListenerAdapter
,我们只需要重写需要处理的回调方法即可。
JModuleLink
支持Spring
模块,默认提供的加载器会针对Spring
环境的模块进行特殊处理,我们可以使用XML
或注解的形式创建Spring
环境的上下文,当然,上下文的创建是加载器根据模块配置进行选择的。JModuleLink
为Spring
模块提供专用的配置类JSpringModuleConfig
,该配置中除了拥有JModuleConfig
的所有属性之外,还拥有一些扩展属性,扩展属性如下:
名称 | 说明 |
---|---|
xmlPatterns | XML配置,默认加载的配置文件:classpath*:META-INF/spring/*.xml 、classpath*:*META-INF/spring/*.xml ,自定义配置文件后,默认配置无效 |
exclusions | 需要排除的配置文件 |
需要注意的是,在模块配置中,如果配置了scanPackages
,则模块加载器会初始化AnnotationConfigApplicationContext
作为上下文,此时xmlPatterns
与exclusions
配置无效,否则使用ClassPathXmlApplicationContext
初始化上下文,加载器会扫描上下文中注入的Action
和监听器
,需要排除的Action
和监听器
的配置是正常生效的,排除的Action
只能作为普通Bean
使用而不能被模块执行。
想要加载器将模块作为Spring
环境加载需要满足以下条件:
JSpringModuleConfig
或JSpringBootModuleConfig
org.springframework.context.support.ClassPathXmlApplicationContext
对象,这里需要分清楚的是,JModuleLink
的运行环境不一定是Spring
环境也可以正常加载作为Spring
环境的模块,主要在模块类路径下或JModuleLink
运行的类路径下扫描到Spring
相关类即可如果加载器检测模块环境以及配置不满足Spring
的加载条件,则会将其作为普通模块加载处理。
与Spring
环境的模块类似,JModuleLinke
也专门提供了对SpringBoot
环境模块的特殊处理,使用SpringBoot
才需要使用JModuleSpringBootConfig
作为配置类,该配置中除了拥有JModuleConfig
的所有属性之外,还拥有一些扩展属性,扩展属性如下:
名称 | 说明 |
---|---|
sources | SpringBoot source |
在SpringBoot
环境下,scanPackages
配置无效,将会将扫描与初始化完全交给SpringBoot
处理,与Spring
环境类似,想要加载器将模块作为SpringBoot
环境加载需要满足以下条件:
JSpringBootModuleConfig
org.springframework.boot.SpringApplication
对象如果加载器检测模块环境以及配置不满足SpringBoot
的加载条件,则会将其作为普通模块加载处理。
JModuleLink
的初衷是实现可插拔、多模块的热部署,对于Web
环境暂时没有做过多的支持,后期可能会增加直接初始化Web
环境模块的增强处理,目前针对Web
环境做了简单的适配,提供了JGenericHttpAction
和JHttpAction
两个用于Web
环境的Action
,这两个实现的关系和HttpServlet
与GenericServlet
非常相似,可以按照Servlet
的开发方式开发Action
。在执行Action
的时候我们只需要使用JHttpHolder
将HttpServletRequest
与HttpServletResponse
进行包装并作为入参传递,就可以使其正常工作。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型