利用spring IOC 实现 模块化开发 动态加载 jar包(包括第三方)不需要重启

2022-06-16 08:37:57

模块化开发的方式有很多 基于rest、webservice 接口 或者服务器集群结合单点登录或者spring cloud 微服务等方式 。无非就是要实现 在不影响现有系统正常运行的情况下 上线 或者下线某些功能模块。更新系统的时候不影响正常不在更新范围内的功能。保证系统稳定。
下面我说一种基于spring IOC 方式实现的动态加载卸载jar文件 来实现模块化开发的功能。
这种方式 下各个模块之间的解耦的 他们在一个 spring factory基础平台下被独立加载 互相之间共享基础平台下的 公有方法 和类库,同时各个模块之间拥有自己独立的第三方jar 或者类库。实现一种 低耦合的模块化。

本次只分享部分核心代码 具体如何切合自己的业务逻辑 还需要各位考量。
核心代码主要是两个类和一个方法

ModuleBeanFactory 类

这个类继承 org.springframework.beans.factory.support.DefaultListableBeanFactory 用于存放当前模块的所有bean对象

构造函数

public ModuleBeanFactory(DefaultListableBeanFactory parentBeanFactory) {
       this.parentBeanFactory=parentBeanFactory;
   }

构造函数中的 parentBeanFactory 是 基础平台的 beanFactory。

 @Override
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        if(this.containsBeanDefinition(name)){
            return super.doGetBean(name, requiredType, args, typeCheckOnly);
        }
        return parentBeanFactory.getBean(name,requiredType,args);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
        return super.getBeanDefinition(beanName);
    }

重写 doGetBean 获取bean对象优先从当前模块的beanfactory中获取 如果当前模块中不存在那么从 基础平台的beanfatory中获取。
这样实现了各个模块之间 共享使用基础平台beanfactory的 bean对象 同时也能使用自己独立加载的bean对象。

ModuleContext 类

这个类主要是用于 创建spring 上下文
继承 org.springframework.context.annotation.AnnotationConfigApplicationContext

只有一个构造函数

public ModuleContext(ModuleBeanFactory beanFactory) {
        super(beanFactory);
    }

loadModule 方法

主要用于从磁盘中读取jar文件 并加载到 ModuleContext 和 ModuleBeanFactory 中。

@Autowired
    private Environment environment;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.act = applicationContext;
    }

    public void loadModule (){
        //创建 beanFactory
        ModuleBeanFactory beanFactory=
                new ModuleBeanFactory(
                        //从 基础类的 applicationContext 获取 beanFactory
                        //applicationContext 获取方法
                        ((AnnotationConfigServletWebServerApplicationContext) act)
                                .getDefaultListableBeanFactory());

        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
        definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        beanFactory.registerBeanDefinition(ConfigurationPropertiesBindingPostProcessor.BEAN_NAME, definition);

        ModuleContext applicationContext=new ModuleContext(beanFactory);
        //获取父节点 类加载器
        URLClassLoader ucl=(URLClassLoader) act.getClassLoader();
        URL[] urls=ucl.getURLs();


        //加载文件所在路径
        // 包含 d:\data\test 下的 jar 包 和 d:\data\test\\lib 下的 第三方jar包
        String baseDir="d:\\data\\test";

        String packages="com.test";//  包文件所在路径
        //加载 基础平台的 jar 包 和 模块化的jar 包 加载遵循双亲委派机制  基础平台有的包 模块里不能重复加载
        StandardExecutorClassLoader classLoader = new StandardExecutorClassLoader(baseDir,
                urls, ucl);
        applicationContext.setClassLoader(classLoader);
        //设置spring 扫描包路径
        applicationContext.scan(packages);
        // 加载 基础平台的 配置文件
        applicationContext.setEnvironment((StandardServletEnvironment)environment);
        applicationContext.refresh();
        //启动这个applicationContext
        applicationContext.start();

        //剩下的逻辑根据业务需要来做 比如获取一个 bean对象
        applicationContext.getBean("beanName");
		或者可以吧这个
    }

以上就是模块化 的核心代码部分 。
关于如何父 子 lassloader共享代码没用出来 具体看源码

源码位置
源码位置:(代码有些问题 不在提供 )
具体在
ahead-frame-schedule 下的 ahead-schedule-modules 模块下
如果有不明白可以联系我。

  • 作者:napcleon1
  • 原文链接:https://blog.csdn.net/napcleon1/article/details/105495937
    更新时间:2022-06-16 08:37:57