介紹
該框架主要是集成於springboot項目,用於開發插件式應用的集成框架。
核心功能
插件配置式插拔於springboot項目。
在springboot上可以進行插件式開發, 擴展性極強, 可以針對不同項目開發不同插件, 進行不同插件jar包的部署。
可通過配置文件指定要啟用或者禁用插件。
支持上傳插件和插件配置文件到服務器, 並且無需重啟主程序, 動態部署插件、更新插件。
支持查看插件運行狀態, 查看插件安裝位置。
無需重啟主程序, 動態的安裝插件、卸載插件、啟用插件、停止插件、備份插件、刪除插件。
在插件應用模塊上可以使用Spring注解定義組件, 進行依賴注入。
支持在插件中開發Rest接口。
支持在插件中單獨定義持久層訪問等需求。
可以遵循主程序提供的插件接口開發任意擴展功能。
插件可以自定義配置文件。目前只支持yml文件。
支持自定義擴展開發接口, 使用者可以在預留接口上擴展額外功能。
利用擴展機制, 定制了SpringBoot-Mybatis擴展包。使用該擴展包, 使用者可以在插件中自定義Mapper接口、Mapper xml 以及對應的實體bean。
源碼地址
https://gitee.com/starblues/springboot-plugin-framework-parent
運行環境
jdk1.8+
apache maven 3.6
mavne 倉庫地址
https://mvnrepository.com/artifact/com.gitee.starblues/springboot-plugin-framework
快速入門
新建項目。
Maven目錄結構下所示
-example
- example-runner
- pom.xml
- example-main
- pom.xml
- example-plugin-parent
- pom.xml
- plugins
- example-plugin1
- pom.xml
- plugin.properties
- example-plugin2
- pom.xml
- plugin.properties
- pom.xml
- pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
結構說明:
pom.xml 代表maven的pom.xml
plugin.properties 為開發環境下, 插件的元信息配置文件, 配置內容詳見下文。
example 為項目的總Maven目錄。
example-runner 在運行環境下啟動的模塊。主要依賴example-main模塊和插件中使用到的依賴包。
example-main 該模塊為項目的主程序模塊。
example-plugin-parent 該模塊為插件的父級maven pom 模塊, 主要定義插件中公共用到的依賴, 以及插件的打包配置。
plugins 該文件夾下主要存儲插件模塊。上述模塊中主要包括example-plugin1、example-plugin2 兩個插件。
example-plugin1、example-plugin2 分別為兩個插件Maven包。
主程序集成步驟
主程序為上述目錄結構中的 example-main 模塊。
在主程序中新增maven依賴包
<dependency>
<groupId>com.gitee.starblues</groupId>
<artifactId>springboot-plugin-framework</artifactId>
<version>${springboot-plugin-framework.version}</version>
</dependency>
1
2
3
4
5
實現並定義配置
實現 com.plugin.development.integration.IntegrationConfiguration 接口。
import com.gitee.starblues.integration.DefaultIntegrationConfiguration;
import org.pf4j.RuntimeMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "plugin")
public class PluginConfiguration extends DefaultIntegrationConfiguration {
/**
* 運行模式
* 開發環境: development、dev
* 生產/部署 環境: deployment、prod
*/
@Value("${runMode:dev}")
private String runMode;
/**
* 插件的路徑
*/
@Value("${pluginPath:plugins}")
private String pluginPath;
/**
* 插件文件的路徑
*/
@Value("${pluginConfigFilePath:pluginConfigs}")
private String pluginConfigFilePath;
@Override
public RuntimeMode environment() {
return RuntimeMode.byName(runMode);
}
@Override
public String pluginPath() {
return pluginPath;
}
@Override
public String pluginConfigFilePath() {
return pluginConfigFilePath;
}
/**
* 重寫上傳插件包的臨時存儲路徑。只適用於生產環境
* @return String
*/
@Override
public String uploadTempPath() {
return "temp";
}
/**
* 重寫插件備份路徑。只適用於生產環境
* @return String
*/
@Override
public String backupPath() {
return "backupPlugin";
}
/**
* 重寫插件RestController請求的路徑前綴
* @return String
*/
@Override
public String pluginRestControllerPathPrefix() {
return "/api/plugins";
}
/**
* 重寫是否啟用插件id作為RestController請求的路徑前綴。
* 啟動則插件id會作為二級路徑前綴。即: /api/plugins/pluginId/**
* @return String
*/
@Override
public boolean enablePluginIdRestControllerPathPrefix() {
return true;
}
public String getRunMode() {
return runMode;
}
public void setRunMode(String runMode) {
this.runMode = runMode;
}
public String getPluginPath() {
return pluginPath;
}
public void setPluginPath(String pluginPath) {
this.pluginPath = pluginPath;
}
public String getPluginConfigFilePath() {
return pluginConfigFilePath;
}
public void setPluginConfigFilePath(String pluginConfigFilePath) {
this.pluginConfigFilePath = pluginConfigFilePath;
}
@Override
public String toString() {
return "PluginArgConfiguration{" +
"runMode='" + runMode + '\'' +
", pluginPath='" + pluginPath + '\'' +
", pluginConfigFilePath='" + pluginConfigFilePath + '\'' +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
配置說明:
runMode:運行項目時的模式。分為開發環境(dev)、生產環境(prod)
pluginPath: 插件的路徑。開發環境建議直接配置為插件模塊的父級目錄。例如: plugins。如果啟動主程序時, 插件為加載, 請檢查該配置是否正確。
pluginConfigFilePath: 在生產環境下, 插件的配置文件路徑。在生產環境下, 請將所有插件使用到的配置文件統一放到該路徑下管理。如果啟動主程序時, 報插件的配置文件加載錯誤, 有可能是該該配置不合適導致的。
uploadTempPath: 上傳插件包時使用。上傳插件包存儲的臨時路徑。默認 temp(相對於主程序jar路徑)
backupPath: 備份插件包時使用。備份插件包的路徑。默認: backupPlugin(相對於主程序jar路徑)
pluginRestControllerPathPrefix: 插件RestController請求的路徑前綴
enablePluginIdRestControllerPathPrefix: 是否啟用插件id作為RestController請求的路徑前綴。啟動則插件id會作為二級路徑前綴。即: /api/plugins/pluginId/**
配置bean
import com.gitee.starblues.integration.*;
import com.gitee.starblues.integration.initialize.AutoPluginInitializer;
import com.gitee.starblues.integration.initialize.PluginInitializer;
import org.pf4j.PluginException;
import org.pf4j.PluginManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PluginBeanConfig {
/**
* 通過默認的集成工廠返回 PluginManager
* @param integrationConfiguration 集成的配置文件
* @return
* @throws PluginException
*/
@Bean
public PluginManager pluginManager(IntegrationConfiguration integrationConfiguration) throws PluginException {
IntegrationFactory integrationFactory = new DefaultIntegrationFactory();
return integrationFactory.getPluginManager(integrationConfiguration);
}
/**
* 定義默認的插件應用。使用可以注入它操作插件。
* @return
*/
@Bean
public PluginApplication pluginApplication(){
return new DefaultPluginApplication();
}
/**
* 初始化插件。此處定義可以在系統啟動時自動加載插件。
* 如果想手動加載插件, 則可以使用 com.plugin.development.integration.initialize.ManualPluginInitializer 來初始化插件。
* @param pluginApplication
* @return
*/
@Bean
public PluginInitializer pluginInitializer(PluginApplication pluginApplication){
AutoPluginInitializer autoPluginInitializer = new AutoPluginInitializer(pluginApplication);
return autoPluginInitializer;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
插件包集成步驟
插件包pom.xml配置說明
以 <scope>provided</scope> 方式引入springboot-plugin-framework包
<dependency>
<groupId>com.gitee.starblues</groupId>
<artifactId>springboot-plugin-framework</artifactId>
<version>${springboot-plugin-framework.version}</version>
<scope>provided</scope>
</dependency>
1
2
3
4
5
6
定義打包配置.主要用途是將 Plugin-Id、Plugin-Version、Plugin-Provider、Plugin-Class、Plugin-Dependencies的配置值定義到META-INF\MANIFEST.MF文件中
<properties>
<plugin.id>springboot-plugin-example-plugin1</plugin.id>
<plugin.class>com.plugin.example.plugin1.DefinePlugin</plugin.class>
<plugin.version>${project.version}</plugin.version>
<plugin.provider>StarBlues</plugin.provider>
<plugin.dependencies></plugin.dependencies>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-assembly-plugin.version>3.1.1</maven-assembly-plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Plugin-Id>${plugin.id}</Plugin-Id>
<Plugin-Version>${plugin.version}</Plugin-Version>
<Plugin-Provider>${plugin.provider}</Plugin-Provider>
<Plugin-Class>${plugin.class}</Plugin-Class>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
在插件包的一級目錄下新建plugin.properties文件(用於開發環境)
新增如下內容(屬性值同步驟1中pom.xml定義的manifestEntries屬性一致):
plugin.id=springboot-plugin-example-plugin1
plugin.class=com.plugin.example.plugin1.DefinePlugin
plugin.version=2.0-SNAPSHOT
plugin.provider=StarBlues
1
2
3
4
配置說明:
plugin.id: 插件id
plugin.class: 插件實現類。見步驟3說明
plugin.version: 插件版本
plugin.provider: 插件作者
1
2
3
4
繼承 com.gitee.starblues.realize.BasePlugin 包
import com.gitee.starblues.realize.BasePlugin;
import org.pf4j.PluginException;
import org.pf4j.PluginWrapper;
public class DefinePlugin extends BasePlugin {
public DefinePlugin(PluginWrapper wrapper) {
super(wrapper);
}
@Override
protected void startEvent() throws PluginException {
}
@Override
protected void deleteEvent() throws PluginException {
}
@Override
protected void stopEvent() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
並且將該類的包路徑(com.plugin.example.plugin1.DefinePlugin)配置在步驟1和2的plugin.class屬性中。
新增HelloPlugin1 controller
此步驟主要驗證環境是否加載插件成功。
@RestController
@RequestMapping(path = "plugin1")
public class HelloPlugin1 {
@GetMapping()
public String getConfig(){
return "hello plugin1"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
運行配置
配置模塊 example-runner 的pom.xml
將主程序的依賴新增到pom.xml 下
將插件中的依賴以 <scope>provided</scope> 方式引入到 pom.xml 下
如下所示:
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.gitee.starblues</groupId>
<artifactId>plugin-example-runner</artifactId>
<version>2.0-RELEASE</version>
<packaging>pom</packaging>
<properties>
<gson.version>2.8.2</gson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.starblues</groupId>
<artifactId>plugin-example-start</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 此處依賴用於解決在開發環境下, 插件包找不到對應依賴包 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
設置idea的啟動
Working directory : D:\xx\xx\springboot-plugin-framework-parent\plugin-example
Use classpath of module: plugin-exampe-runner
勾選: Include dependencies with “Provided” scope
啟動2步驟的配置。
觀察日志出現如下說明加載插件成功。
Plugin 'springboot-plugin-example-plugin1@2.0-RELEASE' resolved
Start plugin 'springboot-plugin-example-plugin1@2.0-RELEASE'
Init Plugins <springboot-plugin-example-plugin1> Success
1
2
3
訪問插件中的Controller 驗證。
瀏覽器輸入:http://ip:port/api/plugins/springboot-plugin-example-plugin1/plugin1
響應並顯示: hello plugin1
說明集成成功!
使用說明
插件中定義配置文件
在插件包的 resources 目錄下定義配置文件 plugin1.yml
name: plugin1
plugin: examplePlugin1
setString:
- set1
- set2
listInteger:
- 1
- 2
- 3
subConfig:
subName: subConfigName
1
2
3
4
5
6
7
8
9
10
11
在代碼中定義對應的bean
import com.gitee.starblues.annotation.ConfigDefinition;
import java.util.List;
import java.util.Set;
@ConfigDefinition("plugin1.yml")
public class PluginConfig1 {
private String name;
private String plugin;
private Set<String> setString;
private List<Integer> listInteger;
private String defaultValue = "defaultValue";
private SubConfig subConfig;
// 自行提供get set 方法
}
public class SubConfig {
private String subName;
public String getSubName() {
return subName;
}
// 自行提供get set 方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
該bean必須加上 @ConfigDefinition(“plugin1.yml”) 注解。其中值為插件文件的名稱。
其他地方使用時, 可以通過注入方式使用。
例如:
@Component("plugin2HelloService")
public class HelloService {
private final PluginConfig1 pluginConfig1;
private final Service2 service2;
@Autowired
public HelloService(PluginConfig1 pluginConfig1, Service2 service2) {
this.pluginConfig1 = pluginConfig1;
this.service2 = service2;
}
public PluginConfig1 getPluginConfig1(){
return pluginConfig1;
}
public String sayService2(){
return service2.getName();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
注意事項
在開發環境:配置文件必須放在resources目錄下。並且@ConfigDefinition(“plugin1.yml”)中定義的文件名和resources下配置的文件名一致。
在生產環境: 該文件存放在pluginConfigFilePath配置的目錄下。
集成擴展
SpringBoot Mybatis 擴展
文檔見: springboot-plugin-framework-extension-mybatis
案例部署
普通例子運行見:package/example
windows環境下運行: package.bat
linux、mac 環境下運行: package.sh
mybatis 案例部署
普通例子運行見:package/example-persistence
windows環境下運行: package.bat
linux、mac 環境下運行: package.sh
sql在 plugin-example-persistence/sql 文件夾下。
生產環境目錄
-main.jar
-main.yml
-plugins
-plugin1.jar
-plugin2.jar
-pluginFile
-plugin1.yml
-plugin2.yml
1
2
3
4
5
6
7
8
9
10
11
12
案例說明
plugin-example:插件基礎功能案例。
plugin-example-persistence: 針對Mybatis集成的案例。
生產環境配置禁用啟用功能
啟用、禁用功能
1.在插件目錄下新建 enabled.txt 文件
2.enabled.txt的內容為:
########################################
# - 啟用的插件
########################################
example-plugin1
1
2
3
4
將需要啟用的插件id配置到文件中。
所有注釋行(以#字符開頭的行)都將被忽略。
禁用功能
1.在插件目錄下新建 disabled.txt 文件
2.disabled.txt的內容為:
########################################
# - 禁用的插件
########################################
example-plugin1
1
2
3
4
將需要啟用的插件id配置到文件中。
所有注釋行(以#字符開頭的行)都將被忽略。
注意事項
插件中代碼編寫完后, 請保證在class文件下的類都是最新編譯的, 再運行主程序, 否則會導致運行的插件代碼不是最新的。
如果啟動時插件沒有加載。請檢查配置文件中的 pluginPath
如果pluginPath 配置為相當路徑,請檢查是否是相對於當前工作環境的目錄。
如果pluginPath配置為絕對路徑,請檢查路徑是否正確。
1
2
3
小技巧
idea 啟動主程序時, 自動編譯插件包的配置
選擇
File->Project Structure->Project Settings->Artifacts->點擊+號->JAR->From modules whith dependencies->選擇對應的插件包->確認OK
啟動配置:
在Before launch 下-> 點擊小+號 -> Build ->Artifacts -> 選擇上一步新增的>Artifacts
版本更新
1.1 版本
新增插件注冊、卸載監聽器。
新增可通過 PluginUser 獲取插件中實現主程序中定義的接口的實現類。
新增插件注冊、卸載時監聽器。
2.0 版本(重大版本更新)
重構代碼。
新增擴展機制。
簡化依賴注入注解, 保持與SpringBoot依賴注入方式一致。
新增插件工廠監聽器、新增插件初始化監聽器(適用於第一次啟動)。
新增插件包Mybatis的集成, 可在插件包中獨立定義Mapper接口、Mapper xml、實體bean。
---------------------
