alibaba jarslink

alibaba jarslink框架教程

demo的github地址:https://github.com/superRabbitMan/jarslink-demo

什么是jarslink

JarsLink (原名Titan)是一个基于JAVA的模块化开发框架,它提供在运行时动态加载模块(一个JAR包)、卸载模块和模块间调用的API。

为什么使用jarslink

隔离性

  • 类隔离:框架为每个模块的Class使用单独的ClassLoader来加载,每个模块可以依赖同一种框架的不同的版本。
  • 实例隔离:框架为每个模块创建了一个独立的Spring上下文,来加载模块中的BEAN,实例化失败不会影响其他模块。
  • 资源隔离:后续会支持模块之间的资源隔离,每个模块使用独立的CPU和内存资源。

动态性

  • 动态发布:模块能在运行时动态加载到系统中,实现不需要重启和发布系统新增功能。支持突破双亲委派机制,在运行时加载父加载器已经加载过的类,实现模块升级依赖包不需要系统发布。
  • 动态卸载:模块能在运行时被动态卸载干净,实现快速下线不需要功能。

易用性

  • 提供了通用灵活的API让系统和模块进行交互。

下载

\1. 官网:https://github.com/alibaba/jarslink

\2. 其它途径:https://oss.sonatype.org/#nexus-search;quick~jarslink

引入项目

至于如何创建一个Maven项目这里不多介绍,自己百度即可

#主要POM,这里请使用1.6以上的版本,因为接下来介绍有个功能1.6极其以上版本才支持



<dependency>



  <groupId>com.alipay.jarslink</groupId>



  <artifactId>jarslink-api</artifactId>



  <version>1.6.1.20180301</version>



</dependency>



#依赖POM



<dependencies>



        <dependency>



            <groupId>org.springframework</groupId>



            <artifactId>spring-aop</artifactId>



            <version>5.0.5.RELEASE</version>



        </dependency>



        <dependency>



            <groupId>org.springframework</groupId>



            <artifactId>spring-web</artifactId>



            <version>5.0.5.RELEASE</version>



            <optional>true</optional>



        </dependency>



        <dependency>



            <groupId>commons-logging</groupId>



            <artifactId>commons-logging</artifactId>



            <version>1.1.3</version>



        </dependency>



        <dependency>



            <groupId>org.apache.commons</groupId>



            <artifactId>commons-lang3</artifactId>



            <version>3.3.2</version>



        </dependency>



        <dependency>



            <groupId>commons-lang</groupId>



            <artifactId>commons-lang</artifactId>



            <version>2.6</version>



        </dependency>



        <dependency>



            <groupId>org.slf4j</groupId>



            <artifactId>slf4j-api</artifactId>



            <version>1.7.7</version>



        </dependency>



        <dependency>



            <groupId>commons-collections</groupId>



            <artifactId>commons-collections</artifactId>



            <version>3.2.1</version>



        </dependency>



        <dependency>



            <groupId>com.google.guava</groupId>



            <artifactId>guava</artifactId>



            <version>17.0</version>



        </dependency>



        <dependency>



            <groupId>junit</groupId>



            <artifactId>junit</artifactId>



            <version>4.12</version>



        </dependency>



    </dependencies>

引入模块jar包

模块jar文件引入是不用buildpath导入项目的,你可以放在CDEF盘任意位置都可以,因为这个模块jar文件是不随项目启动的,我们使用jarslink框架去引用它。

​ 为了方便调用,我放在项目的resources文件下:

img

最重要的对象

<!-- 模块加载引擎,负责加载模块--><bean name="moduleLoader" class="com.alipay.jarslink.api.impl.ModuleLoaderImpl"></bean>
<!-- 模块管理器,负责注册,卸载,查找模块以及执行Action --><bean name="moduleManager" class="com.alipay.jarslink.api.impl.ModuleManagerImpl"></bean>

配置jarslink.xml文件

<?xml version="1.0" encoding="UTF-8"?>



<beans xmlns="http://www.springframework.org/schema/beans"



       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



       xsi:schemaLocation="http://www.springframework.org/schema/beans



       http://www.springframework.org/schema/beans/spring-beans.xsd">



 



       <!--模块加载引擎-->



       <bean id="moduleLoader" name="moduleLoader" class="com.alipay.jarslink.api.impl.ModuleLoaderImpl" />



 



       <!--模块管理器-->



       <bean id="moduleManager" name="moduleManager" class="com.alipay.jarslink.api.impl.ModuleManagerImpl" />



 



</beans>

配置模块信息

public static ModuleConfig buildModuleConfig() {



    URL demoModule = Thread.currentThread().getContextClassLoader().getResource("jarslink-module-demo-1.0.0.jar");//加载模块jar包,可以在任意路径下



    ModuleConfig moduleConfig = new ModuleConfig();



    moduleConfig.setName("hello-world");//设置模块名称



    moduleConfig.setEnabled(true);



    moduleConfig.setVersion("1.0.0");//设置版本



    moduleConfig.setProperties(ImmutableMap.of("svnPath", new Object()));



    moduleConfig.setModuleUrl(ImmutableList.of(demoModule));



    return moduleConfig;



}

第一个调用demo

package com.alibaba.test;



 



import com.alipay.jarslink.api.Action;



import com.alipay.jarslink.api.Module;



import com.alipay.jarslink.api.ModuleLoader;



import com.alipay.jarslink.api.ModuleManager;



import com.alipay.jarslink.api.impl.AbstractModuleRefreshScheduler;



import com.alipay.jarslink.api.impl.ModuleManagerImpl;



import org.junit.Test;



import org.junit.runner.RunWith;



import org.springframework.beans.factory.annotation.Autowired;



import org.springframework.test.context.ContextConfiguration;



import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;



 



import java.util.Map;



import java.util.Set;



 



/**



 * Created by HASEE on 2018/4/22.



 */



@RunWith(SpringJUnit4ClassRunner.class)



@ContextConfiguration(locations = {"classpath*:jarslink.xml"})



public class JarsLinkTest {



 



    @Autowired



    private ModuleLoader moduleLoader;



    @Autowired



    private ModuleManager moduleManager; 



 



    @Test



    public void test1() {



//        System.out.println("moduleLoader = " + moduleLoader);



//        System.out.println("moduleManager = " + moduleManager);



        Module module = moduleLoader.load(ModuleRefreshSchedulerImpl.buildModuleConfig());//加载模块,加载一个模块



        moduleManager.register(module);//注册模块信息



        /*获取指定的Action,执行方式一*/



        Module mod = moduleManager.find("hello-world");



        Map<String, Action> actions = mod.getActions();



//        Set<String> keys = actions.keySet();



//        for (String key : keys) {



//            System.out.println(key + ", " + actions.get(key));



//        }



        Action xmlaction = actions.get("XMLACTION");



        System.out.println(xmlaction.execute("hello world"));



        /*获取指定的Action,执行方式二*/



        //doAction参数:模块中action的名称,action中execute方法的参数



        String result = module.doAction("XMLACTION", "hello world");



        System.out.println(result);



    }



 



}

总结

Jarslink的使用就这么简单,1)加载模块jar。2)注册模块。3)调用模块的Action

开发模块(新建一个maven项目)

​ 模块和Action的关系,一个jar包就是一个模块,模块中有一个或多个Action类,我们调用的就是模块的Action获取功能。所有开发模块其实就是开发Action。

​ Action的开发也比较简单,只要实现com.alipay.jarslink.api.Action<R,T>类即可,Action有2个泛型类,R表示传入参数,T表示返回参数。

​ Action一共要实现两个方法,1)T execute(Rvar1);。2)String getActionName();。execute方法是Action提供的功能方法。getActionName方法是获取的名称,该名称必须唯一,如果不唯一那么将不知道要执行模块的那个Action。

package com.alibaba.action;



 



import com.alipay.jarslink.api.Action;



 



/**



 * Created by HASEE on 2018/4/22.



 */



public class HelloWorldAction implements Action<String, String> {



 



    @Override



    public String execute(String s) {



        return s + ":hello world";



    }



 



    @Override



    public String getActionName() {



        return "hello-world-action";



    }



 



}

配置xml文件

​ 配置文件的存放目录请注意,resources/META-INF/spring/*.xml,加载模块的时候会到这个目录下去读取配置信息。

img

配置信息如下

<?xml version="1.0" encoding="UTF-8"?>



<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"



       xmlns:p="http://www.springframework.org/schema/p"



       xmlns:context="http://www.springframework.org/schema/context"



       xmlns:webflow="http://www.springframework.org/schema/webflow-config"



       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd



         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd



         http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"   default-autowire="byName">


    <context:annotation-config/>


    <!--创建Action-->
    <bean id="helloWorldAction" class="com.alibaba.action.HelloWorldAction"/>

    <bean id="welComeAction" class="com.alibaba.action.WelComeAction" />

</beans>

编译文件然后到处Jar包,这样一个模块就开发完成了,这个模块包含了一个HelloworldAction的类。

​ 将导出的jar包引入到第一个案例的resources文件夹下,就可以调用了。

第二个调用demo

配置自己开发的模块

public static ModuleConfig buildModuleConfig_Demo() {

    URL demoModule = Thread.currentThread().getContextClassLoader().getResource("jarslink-demo-action-1.0-SNAPSHOT.jar");

    ModuleConfig moduleConfig = new ModuleConfig();
    moduleConfig.setName("demo-action");
    moduleConfig.setEnabled(true);
    moduleConfig.setVersion("1.0.0");
    //配置自定义的properties信息
    moduleConfig.setProperties(ImmutableMap.of("svnPath", new Object()));

    moduleConfig.setModuleUrl(ImmutableList.of(demoModule));



    return moduleConfig;



}
package com.alibaba.test;



import com.alipay.jarslink.api.Action;

import com.alipay.jarslink.api.Module;

import com.alipay.jarslink.api.ModuleLoader;

import com.alipay.jarslink.api.ModuleManager;

import com.alipay.jarslink.api.impl.AbstractModuleRefreshScheduler;

import com.alipay.jarslink.api.impl.ModuleManagerImpl;

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Map;
import java.util.Set;



@RunWith(SpringJUnit4ClassRunner.class)


@ContextConfiguration(locations = {"classpath*:jarslink.xml"})
public class JarsLinkTest {

    @Autowired
    private ModuleLoader moduleLoader;


    @Autowired

    private ModuleManager moduleManager; 


    @Test
    public void test2() {

        //加载和注册模块

        Module module = moduleLoader.load(ModuleRefreshSchedulerImpl.buildModuleConfig_Demo());//加载模块,加载一个模块

        moduleManager.register(module);

        String result = module.doAction("hello-world-action", "rabbit");

        System.out.println("result = " + result); 

    }

}

总结

​ 一个模块的Action开发就是这么简单,1)创建自己的Action。2)配置文件中配置bean信息。问题在于每次开发一个Action就要配置文件中加配置文件<beanid=”” name=”” class=”” />,一旦类多了,这样管理比较麻烦,还好在1.6版本中提供了扫描包的功能。

扫描包功能

Action的开发过程是一样的,只不过在xml文件中不需要使用配置信息,而是在调用的时候扫描指定的包即可,下面是关键的代码。

package com.alibaba.test;



 



import com.alipay.jarslink.api.Module;



import com.alipay.jarslink.api.ModuleConfig;



import com.alipay.jarslink.api.ModuleLoader;



import com.alipay.jarslink.api.ModuleManager;



import com.google.common.collect.ImmutableList;



import com.google.common.collect.ImmutableMap;



import org.junit.Test;



import org.junit.runner.RunWith;



import org.springframework.beans.factory.annotation.Autowired;



import org.springframework.test.context.ContextConfiguration;



import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;



import java.net.URL;

/**

 * Created by HASEE on 2018/4/23.

 */



@RunWith(SpringJUnit4ClassRunner.class)



@ContextConfiguration(locations = {"classpath*:jarslink.xml"})



public class AnnoJarsLinkTest {





    @Autowired
    private ModuleLoader moduleLoader;



    @Autowired
    private ModuleManager moduleManager;



    @Test
    public void test1() {

        URL demoModule = Thread.currentThread().getContextClassLoader().getResource("jarslink-demo-anno-action-1.0-SNAPSHOT.jar");

        ModuleConfig moduleConfig = new ModuleConfig();

        moduleConfig.setName("anno-action");

        moduleConfig.setEnabled(true);

        moduleConfig.setVersion("1.0.0");

        moduleConfig.setProperties(ImmutableMap.of("svnPath", new Object()));

        moduleConfig.setModuleUrl(ImmutableList.of(demoModule));

        //扫描模块下的Action

        moduleConfig.addScanPackage("com.alibaba.action");

        Module module = moduleLoader.load(moduleConfig);

        moduleManager.register(module);

        System.out.println("string to long " + module.doAction("string-to-long", "500"));


    }


}

最佳实践

HTTP请求转发

通过请求路径来确定请求的模块和action

private ModuleManager moduleManager;


@RequestMapping(value = "module/{moduleName}/{actionName}/process.json", method = { RequestMethod.GET,RequestMethod.POST })

public Object process(HttpServletRequest request, HttpServletResponse response) {


	Map<String, String> pathVariables = resolvePathVariables(request);



	String moduleName = pathVariables.get("moduleName").toUpperCase()



	String actionName = pathVariables.get("actionName").toUpperCase()



	String actionRequest = XXX;



	return moduleManager.doAction(moduleName,



            actionName, actionRequest);



}


private Map<String, String> resolvePathVariables(HttpServletRequest request) {


        return (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

    }

如何发布模块

有两个方案:

方案1 拉模式

  • 1:在本地编译代码打包成JAR包。
  • 2:把JAR上传到一个远程文件服务器,比如阿里云的OSS。
  • 3:应用从远程服务器下载JAR,可以把配置信息存放在JAR里,比如版本号,JAR名。

方案2 推模式

  • 1:在本地编译代码打包成JAR包。
  • 2:把JAR直接SCP到服务器上的某个目录。
  • 3:应用检查服务指定目录的JAR是否有更新,如果有更新就进行加载。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值