SpringBoot动态加载jar包中的bean

news2024/11/27 12:46:09

一、业务场景

在有些业务场景下,需要SpringBoot来动态加载jar中的class文件,自动往spring容器中添加新的bean;如物联网设备上传的信息用物模型来解析,用java来解析物模型,但用户的设备千差万别,解析设备的物模型不可能包罗万象,设备往物联网平台上传的数据格式也是千差万别,这时就可以让用户自已实现解析物模型的java代码,然后打成jar包,上传到物联网平台,物联网平台就可以解析当前用的设备信息了。

二、准备工作

1、生成的jar需要布署在nginx当中,直接通过url地址可以下载,所以本次使用要用到nginx,nginx的下载地址,放在本博文最后,可以在后面查看下载

在conf文件夹下 nginx的配置如下

server{
        listen      5000;	
        server_name  127.0.0.1;
		location / {
		  root   ./jar;
		  index  index.html index.htm;
		}
    }

启动nginx,双击nginx.exe即可,复制 

http://127.0.0.1:5000/user-1.0.jar

到浏览器下,可以下载user-1.0.jar

三、maven代码结构

 说明:

1、user模块打成jar包,server微服务加载user打成的jar包user-1.0.jar
2、具体原理如下:inter是公共的模块,里面只有一个抽像CommonService,然后user模块里面的teacherService来实现这个抽像类,然把把user打成jar包供server服务来动态加载teacherService,加载后,就可以使用teacherService了
3、user-1.0.jar布署在nginx上,可以直接通过http://127.0.0.1:5000/user-1.0.jar下载,server模中,是直接访问http://127.0.0.1:5000/user-1.0.jar这个地址来读jar包的

四、相关核心代码

1、constroller中加载jar包,执行加载成功的teacherService

@RestController
@RequestMapping("/test")
@Api(tags = "LoadBeanController", description = "测试类")
public class LoadBeanController {

    @Autowired
    private BeanLoadProvider beanLoadProvider;
    @Autowired
    private ContextProvider contextProvider;
    @Autowired
    public Map<String, CommonService> commonServiceFactory;

    @GetMapping("/execute")
    @ApiOperation("执行加载的jar包中的Service")
    public String execute(String serviceName) throws NotFoundException {
        serviceName = StringUtils.uncapitalize(serviceName);
        String str= null;
        Object object = contextProvider.getBean(serviceName);
        if(object==null){
            throw new NotFoundException("未找到对应的service:"+serviceName);
        }else{
            CommonService commonService =(CommonService) object;
            str  = commonService.deal(serviceName);
        }
        return str;
    }

    @GetMapping("/load")
    @ApiOperation("测试loadJar,为jar地址,如布署在nginx上的下载地址http://127.0.0.1:5000/user-1.0.jar")
    public String load(String urlStr) {
        try {
            beanLoadProvider.loadJar(urlStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "成功";
    }

    @GetMapping("/unload")
    @ApiOperation("测试unLoadJar")
    public String unload(String urlStr) {
        try {
            beanLoadProvider.unLoadJar(urlStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "成功";
    }
}

2、加载jar包

/**
     * 加载jar包中的类,并注册到springBean容器中
     */
    public void loadJar(String urlStr){
        URL url=null;
        try {
            url = new URL("jar:" + urlStr + "!/");
            List<String> clazzList = getBeanNames(url);
            URLClassLoader loader = new URLClassLoader(new URL[]{url});
            for(String clazzName:clazzList) {
                try {
                    Class<?> clazz = loader.loadClass(clazzName);
                    registerBean(clazzName,clazz);
                    if(clazzName.endsWith("Controller")){
                        registerController(StringUtils.uncapitalize(clazzName.substring(clazzName.lastIndexOf(".") + 1)));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            reloadSwagger();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 从SpringBean容器中移出已经加载了jar包中的类
     */
    public void unLoadJar(String urlStr){
        URL url=null;
        try {
            url = new URL("jar:" + urlStr + "!/");
            List<String> clazzList = getBeanNames(url);
            URLClassLoader loader = new URLClassLoader(new URL[]{url});
            for(String clazzName:clazzList) {
                try {
                    Class<?> clazz = loader.loadClass(clazzName);
                    if(clazz!=null&&clazzName.endsWith("Controller")){
                        unregisterController(StringUtils.uncapitalize(clazzName.substring(clazzName.lastIndexOf(".") + 1)));
                    }
                    unRegisterBean(clazzName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            reloadSwagger();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public List<String> getBeanNames(URL url){
        List<String> classList = new ArrayList<>();
        try {
            JarURLConnection conn = (JarURLConnection) url.openConnection();
            //JarFile jarFile = new JarFile(new File("d:\\dd\\my.jar"));
            JarFile jarFile = conn.getJarFile(); //获取jarFile
            //解析jar包每一项
            Enumeration<JarEntry> en = jarFile.entries();
            while (en.hasMoreElements()) {
                JarEntry je = en.nextElement();
                String name = je.getName();
                //这里添加了路径扫描限制
                if (name.endsWith(".class")) {
                    String className = name.replace(".class", "").replaceAll("/", ".");
                    System.out.println("className="+className);
                    classList.add(className);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return classList;
    }

3、TeacherService中的代码

@Service
public class TeacherService extends CommonService {

    @Override
    public String deal(String serviceName) {
        System.out.println("老师的实现TeacherService");
        String str = "参数为:"+serviceName;
        System.out.println(str);
        str+=",我是返回加的老师";
        return str;
    }

}

五、测试

1、没中加载jar包的情况下,执行teacherService的deal方法,http://localhost:8080/test/execute?serviceName=teacherService

结果报错,找不到对应的bean,如下截图

 2、加载jar包 http://localhost:8080/test/load?urlStr=http://127.0.0.1:5000/user-1.0.jar

 3、再次执行 http://localhost:8080/test/execute?serviceName=teacherService

 六、完整代码

下载地址:链接:https://pan.baidu.com/s/14JOLJiwQJWieOWgaj8V2BA?pwd=wiux 
提取码:wiux

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647032.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统移植 搭建nfs服务器,启动盘,内核安装和加载

目录 1. nfs 服务器网络环境搭建 1.1. 查看是否安装了 nfs 服务器 1.2. 修改nfs配置文件 1.3. 创建nfs工作目录 1.4. 重启nfs服务 1.5. 开始测试是否成功 2. SD 卡启动盘 2.1. 方法1&#xff1a;从0扇区开始烧写 2.2. 方法2&#xff1a;直接部署 3. Linux 内核的安装…

SpringBoot编程---Day 01

目录 一、springboot介绍 &#xff08;一&#xff09;Spring Boot 特性 &#xff08;二&#xff09;了解自动配置原理 &#xff08;三&#xff09;springboot 入口功能详解 &#xff08;四&#xff09;自定义banner &#xff08;五&#xff09;容器功能 (六)配置文件 二…

(九)枚举器和迭代器(1)

一、枚举器和可枚举类型 复习完了数组之后&#xff0c;由于数组遍历的这个行为&#xff0c;跟枚举器有很大的相关性&#xff0c;所以接下来继续要学习与枚举器相关的内容。 1、使用 foreach 语句 int[] arr1 { 10, 11, 12, 13 };foreach (int item in arr1)//枚举元素Consol…

尚硅谷大数据Flink1.17实战教程-笔记01【Flink概述、Flink快速上手】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷大数据Flink1.17实战教程从入门到精通_哔哩哔哩_bilibili 尚硅谷大数据Flink1.17实战教程-笔记01【Flink概述、Flink快速上手】尚硅谷大数据Flink1.17实战教程-笔记02【Flink部署】尚硅谷…

【JVM 监控工具】性能诊断--JProfiler的使用

文章目录 背景一、Java 性能诊断工具简介二、简单命令行工具三、图形化综合诊断工具JVisualvmJProfiler 四、分布式应用性能诊断五、IDEA中设置JProfilerJProfiler是什么功能安装使用生成快照配置VM运行程序 背景 性能诊断是软件工程师在日常工作中需要经常面对和解决的问题&a…

公司新来的阿里p8,看了我做的APP和接口测试,甩给了我这份文档

移动应用App已经渗透到每个人的生活、娱乐、学习、工作当中&#xff0c;令人激动、兴奋且具有创造性的各种App犹如雨后春笋般交付到用户手中。各类智能终端也在快速发布&#xff0c;而开发者对于全球移动设备的质量和性能却掌握甚少&#xff0c;App与设备的兼容性问题常常导致用…

【状态估计】基于卡尔曼滤波器的传感器直流电机驱动研究(Matlab代码、Simulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Navicat 受邀出席 PostgreSQL 技术峰会,欢迎莅临我们的展台了解 Navicat 工具包如何提升你的工作效能

Navicat 受邀出席 PostgreSQL 技术峰会成都站&#xff0c;欢迎童鞋们莅临我们的展台。你有机会与我们的专家面对面交流&#xff0c;并了解实用的 Navicat 工具包如何帮助PostgreSQL用户&#xff08;应用开发人员、DBA、运维人员以及数据分析师&#xff09;有效地提升日常的工作…

串口控制小车(二次开发)

0.资料 项目工程文件夹 分文件原理 之前的代码 1.L9110S电机驱动模块demo 2.串口通信&#xff08;习题4&#xff1a;PC发送字符串指令给单片机&#xff09; 3.wifi模块&#xff08;串口中断代码优化&#xff09; 3.蓝牙模块 1.串口指令控制小车_分文件 1、和单片机的接…

MVC、MVP、MVVM:详解2

概述 MVC、MVP、MVVM 都是在 Android 开发中经常用到的架构思想&#xff0c;它们都是为了更好地分离代码、提高代码可复用性、方便维护等目的而设计的。下面对这三种架构思想进行简单的介绍和比较。 MVC MVC 架构是最早被使用的一种架构&#xff0c;它把程序分成了三个部分&…

CVPR 2023 首届视觉异常检测(Visual Anomaly and Novelty Detection,VAND)挑战赛 Zero-shot 赛道冠军

这篇文章主要介绍一下我们在 CVPR 2023 VAND Workshop 的挑战赛中所采用的模型和方案。在 Zero-shot 赛道中我们获得了冠军&#xff08;Winner&#xff09;&#xff0c;在 Few-shot 赛道中&#xff0c;我们获得了第四名&#xff08;Honorable Mentions&#xff09;。 题目&…

怎么安装anaconda?anaconda安装详解!

Anaconda Navigator 是 Anaconda 的图形化管理界面&#xff0c;点击它即可进入 Anaconda 的图像化管理界面。许多小伙伴可能被朋友或者小编安利过Anaconda而跃跃欲试&#xff0c;今天小编就将Anaconda安装详解分享给大家。心动的小伙伴们赶紧安装起来吧&#xff01; 下载 官方…

1分钟教你配置好你的python环境

欢迎来到我们的系列博客《Python360全景》&#xff01;在这个系列中&#xff0c;我们将带领你从Python的基础知识开始&#xff0c;一步步深入到高级话题&#xff0c;帮助你掌握这门强大而灵活的编程语法。无论你是编程新手&#xff0c;还是有一定基础的开发者&#xff0c;这个系…

不能发现BUG的测试用例不是好的测试用例吗?

一般情况下技术岗面试都需要经历面试和笔试部分&#xff0c;面试过程中主要采用问答的形式&#xff0c;一般没有完全固定的回答&#xff0c;主要是根据自己的工作经验应答面试官的问题&#xff0c;而笔试部分更注重基础知识以及问题的常规解决方案。下面IT技术宅男为大家整理了…

C++案例

目录 一、while循环猜数组 二、 水仙花数 三、for循环敲桌子游戏 四、99乘法表 五、一维数组--元素逆置 六、冒泡排序 七、封装一个函数--利用冒泡排序&#xff0c;实现对整型数组的升序排序 八、结构体嵌套结构体 九、结构体排序 一、while循环猜数组 说明&#x…

经验总结:13 条自动化测试框架设计原则

1.代码规范 测试框架随着业务推进&#xff0c;必然会涉及代码的二次开发&#xff0c;所以代码编写应符合通用规范&#xff0c;代码命名符合业界标准&#xff0c;并且代码层次清晰。特别在大型项目、多人协作型项目中&#xff0c;如果代码没有良好的规范&#xff0c;那么整个框…

使用@Schedule注解实现定时任务,多线程执行定时任务,Cron表达式详解

Schedule注解实现定时任务&#xff0c;多线程执行定时任务&#xff0c;Cron表达式详解 使用Schedule注解实现定时任务Scheduled注解多线程执行定时任务Cron表达式Cron中的通配符 使用Schedule注解实现定时任务 1、首先&#xff0c;在项目启动类上添加 EnableScheduling 注解&am…

Vue CLI 全局事件总线 消息的订阅与发布

3.10. 全局事件总线&#xff08;GlobalEventBus&#xff09; 一种可以在任意组件间通信的方式&#xff0c;本质上就是一个对象&#xff0c;它必须满足以下条件 所有的组件对象都必须能看见他这个对象必须能够使用$on $emit $off方法去绑定、触发和解绑事件 使用步骤 定义全…

MySQL数据库基础 12

第十二章 MySQL数据类型 1. MySQL中的数据类型2. 整数类型2.1 类型介绍2.2 可选属性2.2.1 M2.2.2 UNSIGNED2.2.3 ZEROFILL 2.3 适用场景2.4 如何选择&#xff1f; 3. 浮点类型3.1 类型介绍3.2 数据精度说明3.3 精度误差说明 4. 定点数类型4.1 类型介绍 5. 位类型&#xff1a;BI…

CSS--Java EE

在前端的代码中&#xff0c;CSS 相关的代码写在什么位置呢&#xff1f; CSS 可以写在<style>标签中外部引入&#xff1a;输入 link: css写在 div 标签中 目录 一、选择器的种类 1 基础选择器 1.1 类选择器 1.2 id选择器 1.3 标签选择器 1.4 通用选择器 小结 2 …