当我们用传统模式动态加载一个jar包时,可能会带来以下麻烦:如所加载的jar包其依赖包与应用产生冲突,或者所加载的jar包运行出现故障时,可能会导致整个应用瘫痪。而JarsLink作为阿里的一款开源框架,正好帮我们解决了这一烦恼,其主要特性如下:
我们以订单系统调用微信支付和支付宝为例来创建一个简单的demo。
1.创建支付宝模块
1)创建一个Maven项目,并在pom.xml中引入依赖文件。
2)开发Action:开发Action就是实现com.alipay.jarslink.api.Action接口,应用在调用各个模块时,其实就是调用Action。com.alipay.jarslink.api.Action源码如下:
public interface Action {
/**
* 处理请求
*
* @param actionRequest 请求对象
*
* @return 响应对象
*/
T execute(R actionRequest);
/**
* 获取Action名称
*
* @return Action名称, 忽略大小写
*/
String getActionName();
}
其中R表示传入参数,T表示返回参数,execute是Action所要执行的方法。getActionName是获取的名称,由于一个模块可包含多个Action,所以每个Action的名称必须唯一。
demo中对Action的实现如下:
@Configuration
public class AliPayAction implements Action{
/**
* 在这里开展各种复杂的业务
*/
public Integer execute(String actionRequest) {
System.out.println("This is Alipay:" + actionRequest);
return 0;
}
/**
* 获取Action名称
*/
public String getActionName() {
return "AliPayAction";
}
}
一个支付宝模块就算创建完毕,然后导出jar包准备调用,简单吧。
2.按同样步骤创建微信支付模块并导出jar包。
3.创建订单模块
1)创建一个Maven项目,并在pom.xml中引入依赖文件。
2)将微信支付和支付宝模块jar包放在resources下
3)配置jarslink.xml文件。
4)调用模块,代码如下:
URL demoModule =Thread.currentThread().getContextClassLoader().getResource("jarslink-alipay-0.0.1.jar");
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setName("AliPayAction");
moduleConfig.setEnabled(true);
moduleConfig.setVersion("0.0.1");
moduleConfig.setProperties(ImmutableMap.of("", new Object()));
moduleConfig.setModuleUrl(ImmutableList.of(demoModule));
moduleConfig.addScanPackage("com.trade.jarslink_alipay");
Module module = moduleLoader.load(moduleConfig);
moduleManager.register(module);
module.doAction("AliPayAction", "ali");
一个简单的JarsLink Demo算是创建完成。
源码下载
#原理
我们从Jarslink源码分析其原理
1.动态加载模块并实现隔离。
从源代码可以看出,通过把模块放到一个新的线程并为其分配新的上下文实现,代码如下:
/**
* 根据本地临时文件Jar,初始化模块自己的ClassLoader,初始化Spring Application Context,同时要设置当前线程上下文的ClassLoader问模块的ClassLoader
*
* @param moduleConfig
* @param tempFileJarURLs
* @return
*/
private ConfigurableApplicationContext loadModuleApplication(ModuleConfig moduleConfig, List
tempFileJarURLs) {
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
//获取模块的ClassLoader
ClassLoader moduleClassLoader = new ModuleClassLoader(moduleConfig.getModuleUrl(), applicationContext
.getClassLoader(), getOverridePackages(moduleConfig));
try {
//把当前线程的ClassLoader切换成模块的
Thread.currentThread().setContextClassLoader(moduleClassLoader);
ConfigurableApplicationContext context;
Properties properties = getProperties(moduleConfig);
Set scanBase = moduleConfig.getScanPackages();
//注解方式加载bean
if (!scanBase.isEmpty()) {
ModuleAnnotationApplicationContext annotationConfigApplicationContext = new
ModuleAnnotationApplicationContext(properties);
annotationConfigApplicationContext.scan(scanBase.toArray(new String[0]));
context = annotationConfigApplicationContext;
} else {
//XML方式加载bean
ModuleXmlApplicationContext moduleApplicationContext = new ModuleXmlApplicationContext();
moduleApplicationContext.setProperties(properties);
moduleApplicationContext.setConfigLocations(findSpringConfigs(tempFileJarURLs, moduleClassLoader,
getExclusionConfigeNameList(properties)));
context = moduleApplicationContext;
}
context.setParent(applicationContext);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("module {}:{} allow current process to override bean in module", moduleConfig.getName(),
moduleConfig.getVersion());
}
((DefaultResourceLoader) context).setClassLoader(moduleClassLoader);
context.refresh();
return context;
} catch (Throwable e) {
CachedIntrospectionResults.clearClassLoader(moduleClassLoader);
throw Throwables.propagate(e);
} finally {
//还原当前线程的ClassLoader
Thread.currentThread().setContextClassLoader(currentClassLoader);
}
}
2.模块管理(即:ModuleManager),从源码可以看出,ModuleManager是通过 ConcurrentHashMap allModules 对其各种操作,代码如下:
public class ModuleManagerImpl implements ModuleManager, DisposableBean {
private static final Logger LOGGER = LoggerFactory
.getLogger(ModuleManagerImpl.class);
/**
* 已注册的所有模块,key:moduleName upperCase
*/
private final ConcurrentHashMap allModules = new ConcurrentHashMap();
private RuntimeModule getRuntimeModule(String name) {
RuntimeModule runtimeModule = allModules.get(name.toUpperCase());
return runtimeModule != null ? runtimeModule : new RuntimeModule();
}
@Override
public List getModules() {
List modules = Lists.newArrayList();
for (String name : allModules.keySet()) {
RuntimeModule runtimeModule = getRuntimeModule((String) name);
for (String version : runtimeModule.getModules().keySet()) {
modules.add(runtimeModule.getModules().get(version));
}
}
return ImmutableList
.copyOf(filter(modules, instanceOf(SpringModule.class)));
}