JarsLink入门

一.JarsLink简介

当我们用传统模式动态加载一个jar包时,可能会带来以下麻烦:如所加载的jar包其依赖包与应用产生冲突,或者所加载的jar包运行出现故障时,可能会导致整个应用瘫痪。而JarsLink作为阿里的一款开源框架,正好帮我们解决了这一烦恼,其主要特性如下:

  1. 模块之间进程隔离,互不影响,避免了依赖包的冲突,当某个模块发生故障时,也不会导致整个系统瘫痪。
  2. 应用可对各个模块实现热部署,如要升级或者新发布某个模块时,无需重启应用。
    目前,JarsLink主要用于蚂蚁金服各项目中,官网地址:https://github.com/alipay/jarslink

二.应用场景列举

  1. 动态加载jar包:当应用在运行时需要动态加载一些jar包时,可用JarsLink替代传统模式。
  2. 集成三方接口:比如系统涉及微信,支付宝,银联等支付时,每个支付接口可作为一个独立的模块,当某个模块需要升级或者发生故障时,不会导致整个系统重启或者瘫痪。
  3. 当系统较复杂需要拆分为多个模块并分配到不同团队开发时,可避免各模块依赖包的冲突,当某个模块需求有变时,不影响其他模块和团队。

三.创建应用

我们以订单系统调用微信支付和支付宝为例来创建一个简单的demo。
1.创建支付宝模块
1)创建一个Maven项目,并在pom.xml中引入依赖文件。
2)开发Action:开发Action就是实现com.alipay.jarslink.api.Action接口,应用在调用各个模块时,其实就是调用Action。com.alipay.jarslink.api.Action源码如下:

public interface Action<R, T> {
    /**
     * 处理请求
     *
     * @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<String, Integer>{
	/**
     *  在这里开展各种复杂的业务
     */
	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<String>
            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<String> 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<String, RuntimeModule> allModules = new ConcurrentHashMap();

    private RuntimeModule getRuntimeModule(String name) {
        RuntimeModule runtimeModule = allModules.get(name.toUpperCase());
        return runtimeModule != null ? runtimeModule : new RuntimeModule();
    }

    @Override
    public List<Module> getModules() {
        List<Module> 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)));
    }
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值