工作项目中用到了这款插件式开发框架,看了一下网上教程不多,所以打算为大家普及一下这个技术框架,写一些入门教程方便大家学习运用。
关于 | About
“Spring-brick”,是一个可以动态扩展系统的框架,最早在2019年开始开发,该框架可以在SpringBoot项目上开发插件功能,开发插件就像开发独立应用一样。
这里的插件我们可以理解成一个独立的SpringBoot应用,微服务等单体项目。
技术文档 | Document
文档如下:https://www.yuque.com/starblues/spring-brick-3.0.0
开源项目 | Project
开源项目:https://gitee.com/starblues/springboot-plugin-framework-parent.git
这个框架除了官方介绍的优点之外,也有它的不足之处,后文我会提出来。
该框架可以在spring-boot
项目上开发出插件功能,在插件中可以和spring-boot
使用方式一模一样。使用了本框架您可以实现如下需求:
- 在插件中,您可以当成一个微型的
spring-boot
项目来开发,简单易用。 - 在插件中扩展出系统各种功能点,用于系统灵活扩展,再也不用使用分支来交付不同需求的项目了。
- 在插件中可以集成各种框架及其各种
spring-boot-xxx-starter
。 - 在插件中可以定义独立依赖包了,再也不用在主程序中定义依赖包了。
- 可以完美解决插件包与插件包、插件包与主程序因为同一框架的不同版本冲突问题了。各个插件可以定义同一依赖的不同版本框架。
- 无需重启主程序,可以自由实现插件包的动态安装部署,来动态扩展系统的功能。
- 插件也可以不依赖主程序独立集成微服务模块。
- 您可以丰富想象该框架给您带来哪些迫切的需求和扩展,以实现系统的低耦合、高内聚、可扩展的优点。
案例 | Demo
本次演示一个入门小案例,让我带大家快速进入这个框架,上手使用它。这个案例是我自己写的一个便于大家快速入门的案例,不容易出现错误,清晰明了的入门教程。
项目结构 | Structure
项目名称: springboot-plugin-framework-example-linghu
├─example-main │ │ pom.xml │ │ │ ├─src │ │ ├─main │ │ │ ├─java │ │ │ │ └─com │ │ │ │ └─linghu │ │ │ │ MainApp.java │ │ │ │ │ │ │ └─resources │ │ │ application.yml │ └─example-plugins-basic │ pom.xml │ ├─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─linghu │ │ │ ├─controller │ │ │ │ ExampleController.java │ │ │ │ ExamplePlugin.java │ │ │ │ │ │ │ └─plugin │ │ └─resources │ │ application.yml
这个项目分为两个部分:
- 主程序-example-main
- 插件程序-example-plugins-basic
我们的目的是运行主程序,然后执行插件程序里的业务,这样我们就可以专注开发插件程序了,可以不断根据业务的不同接入不同的插件程序,而不用去动主程序的代码,这就是我们说的 高内聚、低耦合、可扩展。
分好类以后我们开始接下来的具体工作。
我们的工作分为两个部分:
- 主程序配置集成 | Settings
- 插件配置集成 | Settings
主程序配置集成 | Settings
我们说了主程序实际上就是上面 example-main部分。
引入框架依赖 | Framework
引入框架依赖:
<dependency> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick</artifactId> <version>3.1.0</version> </dependency>
具体主程序的pom文件配置如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.linghu</groupId> <artifactId>springboot-plugin-framework-example-linghu</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>example-main</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
在配置文件加入配置 | Yaml
这里的yaml文件配置主要是让将插件程序接入到主程序中
plugin: runMode: dev mainPackage: com.linghu pluginPath: # 这里集成了一个插件程序example-plugins-basic模块 - ~\example-plugins-basic
需要注意的是上面的 mainPackage
表示主程序类的路径。pluginPath
则是我们插件的相对路径地址。
- ~\example-plugins-basic
中的 ~\
表示相对路径地址; example-plugins-basic
则是我们的插件名称。
完整的yml配置则是:
server: port: 8080 plugin: runMode: dev mainPackage: com.linghu pluginPath: # 这里集成了一个插件程序example-plugins-basic模块 - ~\example-plugins-basic spring: mvc: pathmatch: matching-strategy: ant_path_matcher
SpringBoot启动类改引导类 | Change
这个改动发生在主程序的启动类上
package com.linghu; import com.gitee.starblues.loader.launcher.SpringBootstrap; import com.gitee.starblues.loader.launcher.SpringMainBootstrap; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author linghu * @date ${DATE} ${TIME} */ @SpringBootApplication public class MainApp implements SpringBootstrap { public static void main(String[] args) { // 该处使用 SpringMainBootstrap 引导启动 SpringMainBootstrap.launch(MainApp.class, args); } @Override public void run(String[] args) throws Exception { // 在该实现方法中, 和 SpringBoot 使用方式一致 SpringApplication.run(MainApp.class, args); } }
这里为啥要这么改?后续我会出文章解释,剖析源码。
插件配置集成 | Settings
插件程序主要是指:example-plugins-basic部分
引入依赖 | XML
<!-- spring-boot-starter依赖 --> <!--建议将spring-boot-starter依赖放到第一个位置, 以防止出现依赖冲突导致无法启动插件--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${和主程序一致的springboot版本}</version> </dependency> <!-- spring-brick-bootstrap依赖 --> <dependency> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick-bootstrap</artifactId> <version>${latest.version}</version> </dependency> <!-- 主程序依赖 --> <!-- 将主程序以 provided 方式依赖到插件中 --> <dependency> <groupId>主程序的 groupId</groupId> <artifactId>主程序的 artifactId</artifactId> <version>主程序 version</version> <scope>provided</scope> </dependency>
上面是个引入模版,大家引入以后还要进行修改,下面是我修改的我的完整依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.linghu</groupId> <artifactId>springboot-plugin-framework-example-linghu</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>example-plugins-basic</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- =====================插件配置集成========================--> <!-- spring-boot-starter依赖 --> <!--建议将spring-boot-starter依赖放到第一个位置, 以防止出现依赖冲突导致无法启动插件--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.7.9</version> </dependency> <!--解决方案: 确保插件不包含spring-boot-starter-web依赖,而是让主程序来加载这个依赖。这样,主程序就可以管理所有的Spring Boot web依赖,避免了重复。 如果您使用的是Maven,您可以在插件的pom.xml文件中移除对spring-boot-starter-web的依赖,或者如果您在主程序的pom.xml中已经定义了这个依赖, 您可以在插件的pom.xml中添加<exclusions>元素来排除这个依赖。--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </exclusion> </exclusions> </dependency> <!-- spring-brick-bootstrap依赖 --> <dependency> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick-bootstrap</artifactId> <version>3.1.0</version> </dependency> <!-- 主程序依赖 --> <!-- 将主程序以 provided 方式依赖到插件中 --> <dependency> <groupId>com.linghu</groupId> <artifactId>example-main</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency> </dependencies> <!-- =================插件打包的配置===================--> <build> <plugins> <plugin> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick-maven-packager</artifactId> <version>3.1.0</version> <configuration> <!--当前打包模式为: 开发模式--> <mode>dev</mode> <!--插件信息定义--> <pluginInfo> <!--插件id--> <id>example-plugins-basic</id> <!--插件入口类, 定义说明见: 定义插件入口类--> <bootstrapClass>com.linghu.controller.ExamplePlugin</bootstrapClass> <!--插件版本号--> <version>1.0.0-SNAPSHOT</version> </pluginInfo> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
定义插件引导类 | Class
这个引导类是加在插件里的主类里的,不要搞错了。
package com.linghu.controller; import com.gitee.starblues.bootstrap.SpringPluginBootstrap; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author linghu * @date 2024/3/15 9:55 */ @SpringBootApplication public class ExamplePlugin extends SpringPluginBootstrap { public static void main(String[] args) { new ExamplePlugin().run(args); } }
定义插件main
入口类, 继承SpringPluginBootstrap
类, 然后在main
函数中实例化当前引导类,并执行run
方法即可。
配置Maven打包插件 | Maven
其实这一步工作我已经在上面提到了,也就是在引入依赖部分提供了。
<!-- =================插件打包的配置===================--> <build> <plugins> <plugin> <groupId>com.gitee.starblues</groupId> <artifactId>spring-brick-maven-packager</artifactId> <version>3.1.0</version> <configuration> <!--当前打包模式为: 开发模式--> <mode>dev</mode> <!--插件信息定义--> <pluginInfo> <!--插件id--> <id>example-plugins-basic</id> <!--插件入口类, 定义说明见: 定义插件入口类--> <bootstrapClass>com.linghu.controller.ExamplePlugin</bootstrapClass> <!--插件版本号--> <version>1.0.0-SNAPSHOT</version> </pluginInfo> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
新增Controller接口API | API
其实这个接口我们就加在插件程序里就行了
package com.linghu.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author linghu * @date 2024/3/15 9:58 */ @RestController @RequestMapping("/example") public class ExampleController { @GetMapping public String hello(){ return "hello"; } }
注意一下Controller位置,放错了很容易导致后续Controller加载不到主程序的:
编译打包插件 | JAR
这里打包有两种方法:
- 插件使用
maven
命令:mvn clean package
进行编译 - 通过Maven插件、先点击clean、在点击package
我用的第二种方法打包,如图:
打包完成以后,观察target文件下会出现jar包:
启动测试 | Main
这个测试是直接启动主程序的
- 启动主程序
main
, 日志中出现如下关键内容,说明集成插件成功
如上我们还知道了如下信息,那就是我们请求测试的接口地址为:
http://127.0.0.1:8080/plugins/example-plugins-basic/
打开浏览器,访问: http://127.0.0.1:8080/plugins/example-plugins-basic/example
出现下图说明访问接口测试成功。
Demo案例Git地址 | Gitee
这个教程属于入门级别的,没有对源码,原理展开讲解,后续有时间会接着更的,这里主要是教大家快速上手,遇到不懂的,欢迎评论区提出。
完整项目demo:
《SpringBrick-springboot-plugin-framework-example-linghu》
项目下载以后记得要用Maven插件打一下jar包,可以直接运行:
mvn clean package
进行编译~