使用网络classloader 实现业务功能动态修改加载

日常中我们很多时候是需要进行class的动态加载的而且不希望影响业务,对于java 应用我们
可以开发自己的类加载器可以方便的解决,使用网络类加载器就更加灵活了,可以更好的控制
类的版本以及权限控制,而且灵活性很高(类似rpc,但是运行时还是单体的,rpc 的jvm运行时是跨
主机的)

参考图

 

 

原理说明

还是基于契约的开发模式,包含了一个通用的common-contract 的模块(可以基于抽象以及接口定义)
cust jars 模块主要是基于契约开发的功能模块,开发完成之后基于ci/cd 直接部署到s3中(可以支持多版本
以及访问安全),对于业务入口可以基于网络类加载器加载s3 的jars,然后就可以动态的进行class 的运行加载了

一个参考实例

  • 项目结构

 

 

  • 契约模块
    就是基于接口定义的
 
package com.dalong;
public interface LoginService {
    String printName();
    String printNameVersion(Integer version);
}
  • 契约实现

    注意maven 模块需要引入契约定义

package com.dalong;
 
public class MyApp  implements  LoginService{
    @Override
    public  String printName(){
       return MyUtils.randomUserName();
    }
 
    @Override
    public String printNameVersion(Integer version) {
        return MyUtils.randomUserNameVersion(version);
    }
}
 
 
  • 应用入口
    一个简单spring boot web 项目,服务动态加载部分
 
package com.dalong;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.xeustechnologies.jcl.JarClassLoader;
import org.xeustechnologies.jcl.JclObjectFactory;
import org.xeustechnologies.jcl.ProxyClassLoader;
 
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
 
@Component
 
public class LoginServiceA {
    private JarClassLoader jcl = new JarClassLoader();
    private Logger logger  = LoggerFactory.getLogger(LoginServiceA.class);
    public String printName() throws MalformedURLException {
        logger.info("load class");
        //  每次都加载,当然我们可以自己cache,这样就不用每次都下载了,加载了
        JarClassLoader jcl = new JarClassLoader();
        //Loading classes from different sources
        jcl.add(new URL("http://localhost:8080/userlogin-1-SNAPSHOT.jar"));
        JclObjectFactory factory = JclObjectFactory.getInstance();
        //Create object of loaded class
        logger.info("load class from {}","http://localhost:8080/userlogin-1-SNAPSHOT.jar");
        LoginService obj = (LoginService)factory.create(jcl, "com.dalong.MyApp");
        return obj.printName();
    }
 
    public String printNameVersion(Integer version) throws MalformedURLException {
        //Loading classes from different sources
        JarClassLoader jcl = new JarClassLoader();
        //  每次都加载,当然我们可以自己cache,这样就不用每次都下载了,加载了,此处模拟了多版本的能力
        String requestURL = String.format("http://localhost:8080/userlogin-%d-SNAPSHOT.jar",version);
        jcl.add(new URL(requestURL));
        logger.info("load class from {}",requestURL);
        JclObjectFactory factory = JclObjectFactory.getInstance();
        //Create object of loaded class
        LoginService obj = (LoginService)factory.create(jcl, "com.dalong.MyApp");
        return obj.printNameVersion(version);
    }
}

运行效果

对于契约实现为了简单可以直接暴露到一个静态服务器中就行

 

 

说明

对于开发来说,基于抽象类比接口更加好,因为接口都是需要实现的,抽象类就不一样了,好处是我们可以保证业务代码的二进制兼容,不然每次反而维护很复杂
以上是一个简单的示例,可以扩展下,实现比较强大的业务规则处理

参考资料

https://github.com/sofastack/sofa-ark
https://github.com/kamranzafar/JCL
https://github.com/rongfengliang/jcl-classloader-learning

posted on 2022-01-13 23:57  荣锋亮  阅读(165)  评论(0编辑  收藏  举报

导航