自定义maven插件,在项目中命令启动springboot并加载当前项目资源
背景
最近在制定团队内公用的基础框架,基于单应用多module的架构思路,使用maven管理项目依赖,在项目中定义了一个springboot模块,该模块依赖具体的业务实现模块,启动后通过扫描路径下的类加载服务,业务开发同事只需要开发具体业务模块即可。
但是在项目管理时,不期望业务开发同事关心和修改基础框架。最开始的做法是让业务开发同事在项目的module管理模块下新建业务module模块(见下描述),在不同分支开发不同的业务,开发自测的时候,需要在springboot模块中依赖具体业务module并启动。
gm-admin --------------------------------管理后台(springboot)服务,依赖gm-modules中的具体实现
gm-common--gm-common-core ----------------------基础包,含最基础的基类、工具类、异常类等--gm-common-log ----------------------日志实现包,通过注解,记录web调用参数和结果 --gm-common-ratelimit -----------------限流器实现,若需对用户进行限制则需要依赖gm-common-security模块 --gm-common-redis ---------------------redis缓存依赖和分布式锁工具类--gm-common-security ------------------基础安全模块,校验和设置用户信息、权限--gm-common-sftp ----------------------sftp工具 --gm-common-storage -------------------对象存储实现,目前支持本地存储和腾讯oss
gm-framework ----------------------------框架模块,主要实现数据源注入等
gm-gateway ------------------------------网关服务,实现报文加解密、限流、路由等
gm-modules--gm-mall -----------------------------商城模块--gm-partner --------------------------合伙人项目使用,非商城内容--gm-system ---------------------------系统管理模块,实现系统用户、角色、权限、菜单、机构等业务逻辑--gm-third ----------------------------第三方服务模块,实现对第三方服务的调用,如短信、积分--gm-wechat ---------------------------微信模块,实现微信用户授权、生成小程序码等与微信交互逻辑
这样的开发模式导致业务开发的同事实际上需要在“框架项目”中进行业务开发,虽然采用了module进行划分,但是在开发中遇到问题总会尝试或者难免会对框架代码进行修改、优化,最终导致冲突等问题。而我想达到的效果是“框架代码”对业务开发同事尽量透明,类似于之前使用dapeng soa框架开发应用时一样,不需要关心容器是怎么跑起来的,只需要关心本业务自身的业务和依赖。
大体思路是:开发一个自定义maven插件,将业务代码在单独的项目中进行开发,需要启动项目时,在项目目录下执行mvn命令,执行maven插件,这个插件会将当前项目的(类)资源和依赖的依赖包添加到类加载器,并启动springboot项目,实现在springboot项目启动当前项目的目的。
实现
具体代码步骤如下供参考:
Maven插件项目pom配置:
<packaging>maven-plugin</packaging>
<name>gm-maven-plugin</name><dependencies>...<!-- SpringBoot容器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.5.10</version></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-core</artifactId><version>3.5.2</version><scope>provided</scope></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-annotations</artifactId><version>3.5.2</version><scope>provided</scope></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-project</artifactId><version>2.2.1</version></dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-plugin-plugin</artifactId><version>3.5</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins>
</build>
</dependencies>
关键代码:
@Mojo(name = "run", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
public class GmMavenPlugin extends AbstractMojo {
/*** 获取项目编译环境类路径*/
@Parameter(defaultValue = "${project}", readonly = true)
protected MavenProject project;@Overridepublic void execute() throws MojoExecutionException {try {// 获取应用程序的 classpathList<String> classpathElements = project.getRuntimeClasspathElements();URL[] urls = new URL[classpathElements.size()];for (int i = 0; i < classpathElements.size(); i++) {urls[i] = new File(classpathElements.get(i)).toURI().toURL();System.out.println("URL: " + urls[i]);}// // 创建一个新的 ClassLoaderClassLoader classLoader = URLClassLoader.newInstance(urls, Thread.currentThread().getContextClassLoader());Class<?> applicationClass = classLoader.loadClass("com......GmAdminApplication");SpringApplication application = new SpringApplication(applicationClass);application.setResourceLoader(new DefaultResourceLoader(classLoader));application.setMainApplicationClass(applicationClass);application.run();// 挂起当前线程Thread.currentThread().join();} catch (Exception e) {throw new MojoExecutionException("Failed to start Spring Boot application", e);}}
...
编写完成后将该插件install到本地仓库(或推送到远端私库)。
新建一个业务项目,完成业务代码的开发和编译,如下:
@RestController
@RequestMapping("/tracking")
public class TrackingController {private static final Logger logger = LoggerFactory.getLogger(TrackingController.class);@PostConstructpublic void postConstruct() {logger.info("--------------------------------------------");logger.info("Tracking模块控制器被加载...");logger.info("--------------------------------------------");}
...
然后在项目目录下执行maven命令
mvn compile com.dt26:gm-maven-plugin:2.0.0-SNAPSHOT:run
项目即在springboot容器中启动,并可以看到日志如下:
...
[INFO] --------------------------------------------
[INFO] Tracking 模块控制器被加载...
[INFO] --------------------------------------------
[INFO] Exposing 1 endpoint(s) beneath base path '/actuator'
[INFO] Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
[INFO] Will not secure any request
[INFO] Starting ProtocolHandler ["http-nio-8080"]
[INFO] Tomcat started on port(s): 8080 (http) with context path '/admin'
...
整体的需求就算满足了,剩下就是优化代码使得更优雅。当前只能实现开发时启动项目,在实际打包发布到测试环境和生产时,仍然需要gm-admin项目中引入业务代码模块并打包成可执行包。这一步后续可以考虑使用maven命令等方式自动完成。
参考:https://www.cnblogs.com/coder-chi/p/11305498.html
相关文章:
自定义maven插件,在项目中命令启动springboot并加载当前项目资源
背景 最近在制定团队内公用的基础框架,基于单应用多module的架构思路,使用maven管理项目依赖,在项目中定义了一个springboot模块,该模块依赖具体的业务实现模块,启动后通过扫描路径下的类加载服务,业务开发…...
Linux系统【Centos7】更新内核更新软件详细教程
更新内核: 1. 打开终端,输入命令 sudo yum update,等待更新完成。 2. 重启系统,输入命令 sudo reboot。 3. 在 GRUB 引导界面,选择最新的内核版本,按下回车键进入系统。 4. 在终端中输入命令 uname -r&…...
C++ 中new/delete与malloc/free详解
文章目录前言一、new/delete1. 序言2. 使用方法2.1. new 和 delete 基本语法2.2. new 和 delete 的底层实现原理3. 底层原理3.1. operator new 和 operator delete3.2. new 和 delete 的底层实现原理4. 注意事项5. 总结二、malloc/free1. 序言2. 使用方法2.1. malloc 和 free 基…...

crm软件哪个好?该如何选择?
crm软件哪个好?该如何选择? 首先我们需要明确一下什么是好的CRM系统,优质的CRM系统应该具备以下优势: 1)提高销售效率:通过CRM系统,销售人员可以跟踪客户互动历史和交易记录,了解客…...

蓝桥杯第22天(Python)(疯狂刷题第5天)
题型: 1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要…...

软件测试面试常问的问题有哪些?
互联发展是很快的,每年都会有新语言的诞生。 我干测试已经三年了,主要负责web功能测试,java编写接口自动化,APP功能测试,APP 接口自动化(也是用的java),面过得测试也差不多30个&…...
js之文件信息读取篇高级基础
文章目录js之文件信息读取(FileReader)获取文件相关信息的两种方式js原生拖拽事件js之文件信息读取(FileReader) 首先这里面会讲一些知识点 bolb 对象FileReader对象 let blob new Blob([heewwekgewgwer], { type: text/plain …...

SQL Server的死锁说明
死锁指南一、了解死锁二、检测并结束死锁2.1、可能死锁的资源三、处理死锁四、最大限度地减少死锁4.1、以相同的顺序访问对象4.2、避免事务中的用户交互4.3、保持交易简短且在一个批次中4.4、使用较低的隔离级别4.5、使用基于行版本控制的隔离级别4.6、使用绑定连接4.7、停止事…...
关于#define的一些小知识
目录 一,#define的声明格式: 二,#define宏的作用是为了完成替换 #define的替换规则: 三,#define使用时常犯的错误 四,宏与函数的比较 4.1,什么时候使用宏? 4.1,…...

rabbitmq普通集群与镜像集群搭建
1.准备三台centos7主机,并关闭防火墙与selinux 2.安装rabbitmq环境必备的Erlang(以下所有操作三台主机均需要执行) 执行以下命令下载配置erlang yum源 curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash使用yum命…...
session和jwt哪个更好
session和jwtsession优点缺点jwt优点缺点总结session 优点 原理简单,易于学习。用户信息存储在服务端,可以快速封禁某个用户。 缺点 占用服务端内存,硬件成本高。多进程,多服务器时,不好同步-需要使用第三方缓存&a…...

基于TPU-MLIR实现UNet模型部署-决赛答辩02
队伍:AP0200023 目录 初赛 一、 模型导出优化 1.1 直接倒出原始模型并转换 1.2 导出模型前处理 1.2.1 导出Resize 1.2.2 导出归一化 1.3导出模型后处理 1.3.1导出 Resize 与 1.3.2导出 ArgMaxout 1.3.3导出特征转RGB 复赛 一、 确定baseline 二、优化模…...

Maven高级-分模块开发依赖管理
Maven高级-分模块开发&依赖管理1,分模块开发1.1 分模块开发设计1.2 分模块开发实现1.2.1 环境准备1.2.2 抽取domain层步骤1:创建新模块步骤2:项目中创建domain包步骤3:删除原项目中的domain包步骤4:建立依赖关系步骤5:编译maven_02_ssm项目步骤6:将项目安装本地…...

《安富莱嵌入式周报》第308期:开源带软硬件安全认证的PLC设计,开源功率计,可靠PID实现,PR2机器人设计文件全开源,智能手表设计WASP-OS
周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版: https://www.bilibili.com/video/BV1F24y157QE 《安富莱嵌入式周报》第308期:开源带软…...

代码随想录算法训练营第五十六天 | 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结
583. 两个字符串的删除操作 动规五部曲 1、确定dp数组(dp table)以及下标的含义 dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。 2、确定递推…...

Sip协议
简介 SIP(Session Initiation Protocol,会话初始协议)是一个用于建立、更改和终止多媒体会话的应用 层控制协议,其中的会话可以是 IP 电话、多媒体会话或多媒体会议。SIP 是 IETF 多媒体数据和控 制体系结构的核心协议࿰…...
RandomAccessFile类 断点续传
文章目录学习链接RandomAccessFile构造方法实现的接口DataOutputDataInputAutoCloseable重要的方法多线程读写同一个文件(多线程复制文件)代码1代码2断点续传FileUtils学习链接 RandomAccessFile详解 Java IO——RandomAccessFile类详解 java多线程-断点…...
SpringCloud微服务技术栈的注册中心Eureka
文章目录SpringCloud微服务技术栈的注册中心Eureka简介Eureka特点操作步骤环境准备创建Eureka Server注册服务提供方调用服务消费方总结SpringCloud微服务技术栈的注册中心Eureka 简介 在微服务架构中,服务的数量庞大,而且每个服务可能会有多个实例。此…...
Unity最新热更新框架 hybridclr_addressable
GitHub:YMoonRiver/hybridclr_addressable: 开箱即用的商业游戏框架,集成了主流的开发工具。将主流的GameFramework修改,支持Addressable和AssetBundle,已完善打包工具和流程。 (github.com) # 新增GameFramework Addressables 开箱即用 # 新…...

【c语言】一维数组***特性、存储原理
创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...