该框架主要是集成于springboot项目,用于开发插件式应用的集成框架。
支持在插件中自定义Mapper接口、Mapper xml 以及对应的实体bean。
支持实体bean的别名。
支持集成Mybatis-Plus。
详见 插件SpringBoot Mybatis扩展
支持通过http访问插件中静态资源。
详见 插件静态资源访问扩展
https://gitee.com/starblues/springboot-plugin-framework-parent
https://mvnrepository.com/artifact/com.gitee.starblues/springboot-plugin-framework
https://gitee.com/starblues/springboot-plugin-framework-parent/wikis/pages
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
结构说明:
主程序为上述目录结构中的 example-main 模块。
<dependency>
<groupId>com.gitee.starbluesgroupId>
<artifactId>springboot-plugin-frameworkartifactId>
<version>${springboot-plugin-framework.version}version>
dependency>
最新版本:
<dependency>
<groupId>com.gitee.starbluesgroupId>
<artifactId>springboot-plugin-frameworkartifactId>
<version>2.1.2-RELEASEversion>
dependency>
实现 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 + '\'' +
'}';
}
}
配置说明:
runMode:运行项目时的模式。分为开发环境(dev)、生产环境(prod)
pluginPath: 插件的路径。开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。
pluginConfigFilePath: 在生产环境下, 插件的配置文件路径。在生产环境下, 请将所有插件使用到的配置文件统一放到该路径下管理。如果启动主程序时, 报插件的配置文件加载错误, 有可能是该该配置不合适导致的。
uploadTempPath: 上传插件包时使用。上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)
backupPath: 备份插件包时使用。备份插件包的路径。默认: backupPlugin(相对于主程序jar路径)
pluginRestControllerPathPrefix: 插件RestController请求的路径前缀
enablePluginIdRestControllerPathPrefix: 是否启用插件id作为RestController请求的路径前缀。启动则插件id会作为二级路径前缀。即: /api/plugins/pluginId/**
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;
}
}
以
方式引入springboot-plugin-framework包
<dependency>
<groupId>com.gitee.starbluesgroupId>
<artifactId>springboot-plugin-frameworkartifactId>
<version>${springboot-plugin-framework.version}version>
<scope>providedscope>
dependency>
定义打包配置.主要用途是将 Plugin-Id、Plugin-Version、Plugin-Provider、Plugin-Class、Plugin-Dependencies
的配置值定义到META-INF\MANIFEST.MF
文件中
<properties>
<plugin.id>springboot-plugin-example-plugin1plugin.id>
<plugin.class>com.plugin.example.plugin1.DefinePluginplugin.class>
<plugin.version>${project.version}plugin.version>
<plugin.provider>StarBluesplugin.provider>
<plugin.dependencies>plugin.dependencies>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<maven-compiler-plugin.version>3.7.0maven-compiler-plugin.version>
<maven-assembly-plugin.version>3.1.1maven-assembly-plugin.version>
properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<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.pluginsgroupId>
<artifactId>maven-assembly-pluginartifactId>
<version>${maven-assembly-plugin.version}version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
<archive>
<manifest>
<addDefaultImplementationEntries>trueaddDefaultImplementationEntries>
<addDefaultSpecificationEntries>trueaddDefaultSpecificationEntries>
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-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
manifestEntries
属性一致):plugin.id=springboot-plugin-example-plugin1
plugin.class=com.plugin.example.plugin1.DefinePlugin
plugin.version=2.0-SNAPSHOT
plugin.provider=StarBlues
配置说明:
plugin.id: 插件id
plugin.class: 插件实现类。见步骤3说明
plugin.version: 插件版本
plugin.provider: 插件作者
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() {
}
}
并且将该类的包路径(com.plugin.example.plugin1.DefinePlugin)配置在步骤1和2的plugin.class属性中。
此步骤主要验证环境是否加载插件成功。
@RestController
@RequestMapping(path = "plugin1")
public class HelloPlugin1 {
@GetMapping()
public String getConfig(){
return "hello plugin1"
}
}
provided
方式引入到 pom.xml 下如下所示:
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.3.RELEASEversion>
<relativePath/>
parent>
<groupId>com.gitee.starbluesgroupId>
<artifactId>plugin-example-runnerartifactId>
<version>2.0-RELEASEversion>
<packaging>pompackaging>
<properties>
<gson.version>2.8.2gson.version>
properties>
<dependencies>
<dependency>
<groupId>com.gitee.starbluesgroupId>
<artifactId>plugin-example-startartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>${gson.version}version>
<scope>providedscope>
dependency>
dependencies>
project>
Working directory : D:\xx\xx\springboot-plugin-framework-parent\plugin-example
Use classpath of module: plugin-exampe-runner
勾选: Include dependencies with “Provided” scope
观察日志出现如下说明加载插件成功。
Plugin '[email protected]' resolved
Start plugin '[email protected]'
Init Plugins <springboot-plugin-example-plugin1> Success
浏览器输入:http://ip:port/api/plugins/springboot-plugin-example-plugin1/plugin1
响应并显示: hello plugin1
说明集成成功!
name: plugin1
plugin: examplePlugin1
setString:
- set1
- set2
listInteger:
- 1
- 2
- 3
subConfig:
subName: subConfigName
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 方法
}
该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();
}
}
在开发环境:配置文件必须放在resources目录下。并且@ConfigDefinition(“plugin1.yml”)中定义的文件名和resources下配置的文件名一致。
在生产环境: 该文件存放在pluginConfigFilePath
配置的目录下。
插件之间的数据交互功能, 是在同一JVM运行环境下, 基于代理、反射机制完成方法调用。使用说明如下:
@Supplier("SupplierService")
public class SupplierService {
public Integer add(Integer a1, Integer a2){
return a1 + a2;
}
}
@Caller("SupplierService")
public interface CallerService {
Integer add(Integer a1, Integer a2);
}
3.被调用者和调用者也可以使用注解定义被调用的方法。例如:
被调用者:
@Supplier("SupplierService")
public class SupplierService {
@Supplier.Method("call")
public String call(CallerInfo callerInfo, String key){
System.out.println(callerInfo);
return key;
}
}
调用者:
@Caller("SupplierService")
public interface CallerService {
@Caller.Method("call")
String test(CallerInfo callerInfo, String key);
}
该场景主要用于参数类型不在同一个地方定义时使用。比如 被调用者的参数类: CallerInfo 定义在被调用者的插件中, 调用者的参数类: CallerInfo 定义在调用者的插件中。就必须配合 @Supplier.Method("")、@Caller.Method("") 注解使用, 否则会导致NotFoundClass 异常。
如果调用者没有使用注解 @Caller.Method("") 则默认使用方法和参数类型来调用。
4.对于3步骤中问题的建议
可以将被调用者和调用者的公用参数和返回值定义在主程序中、或者单独提出一个api maven包, 然后两者都依赖该包。
5.案例位置
pluging-example:
com.plugin.example.plugin1.service.SupplierService
com.plugin.example.plugin2.service.CallerService
com.plugin.example.plugin2.rest.ProxyController
文档见: springboot-plugin-framework-extension-mybatis
普通例子运行见:package/example
windows环境下运行: package.bat
linux、mac 环境下运行: package.sh
普通例子运行见: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
plugin-example:插件基础功能案例。
plugin-example-persistence: 针对Mybatis集成的案例。
1.在插件目录下新建 enabled.txt
文件
2.enabled.txt的内容为:
########################################
# - 启用的插件
########################################
example-plugin1
将需要启用的插件id配置到文件中。
所有注释行(以#字符开头的行)都将被忽略。
1.在插件目录下新建 disabled.txt
文件
2.disabled.txt的内容为:
########################################
# - 禁用的插件
########################################
example-plugin1
将需要启用的插件id配置到文件中。
所有注释行(以#字符开头的行)都将被忽略。
如果pluginPath 配置为相当路径,请检查是否是相对于当前工作环境的目录。
如果pluginPath配置为绝对路径,请检查路径是否正确。
<dependency>
<groupId>com.gitee.starbluesgroupId>
<artifactId>springboot-plugin-frameworkartifactId>
<version>${springboot-plugin-framework.version}version>
<exclusions>
<exclusion>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
exclusion>
<exclusion>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
exclusion>
exclusions>
dependency>
启动配置:
在Before launch 下-> 点击小+号 -> Build ->Artifacts -> 选择上一步新增的>Artifacts
859570617
使用场景: 在主程序中定义了接口, 插件中存在实现了该接口的实现类, 通过PluginUser 的 getPluginBeans(接口Class) 可以获取所有插件中实现该接口的实现类。具体详见源码。