spring-plugin 是spring 官方提供的一个插件化设计方案,比如使用支持基于spring 的项目
项目结构
此简单测试项目是一个maven 多模块的,包含了一个plugin 契约模块,连个插件实现,以及一个bootstrap 启动入口
- 代码结构
├── README.md
├── bootstrap
│ ├── HELP.md
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── dalongplugin
│ │ └── bootstrap
│ │ ├── BootstrapApplication.java
│ │ └── demos
│ │ └── web
│ │ └── Api.java
│ └── resources
│ ├── application.properties
│ └── static
│ └── index.html
├── plugin_a
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── dalongplugina
│ │ │ └── PluginA.java
│ │ └── resources
│ └── test
│ └── java
├── plugin_b
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── dalongpluginb
│ │ │ └── PluginB.java
│ │ └── resources
│ └── test
│ └── java
├── plugin_interface
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── dalongpluginb
│ │ │ └── PluginContract.java
│ │ └── resources
│ └── test
│ └── java
└── pom.xml
代码说明
- plugin_interface
就是一个接口定义
package com.dalongpluginb;
import org.springframework.plugin.core.Plugin;
public interface PluginContract extends Plugin<String> {
String message(String input);
}
引用的包
<dependencies>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
</dependencies>
- plugina 实现
package com.dalongplugina;
import com.dalongpluginb.PluginContract;
import org.springframework.stereotype.Component;
@Component
public class PluginA implements PluginContract {
@Override
public String message(String input) {
return String.format("plugina: %s", input);
}
@Override
public boolean supports(String s) {
return s.equalsIgnoreCase("plugina");
}
}
maven 依赖
<dependencies>
<dependency>
<groupId>com.dalong</groupId>
<artifactId>plugin_interface</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
pluginb 类似
- bootstrap 入口
一个spring boot 项目,同时会添加插入契约定义,默认是一个spring boot web 项目
maven 依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dalong</groupId>
<artifactId>bootstrap</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>bootstrap</name>
<description>bootstrap</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.2</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
// spring-plugin 依赖添加
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-metadata</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.dalong</groupId>
<artifactId>plugin_a</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.dalong</groupId>
<artifactId>plugin_b</artifactId>
<version>1.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.dalong</groupId>
<artifactId>plugin_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
// 移除jar maven 构建信息
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
// 为了方便插件的加载没有使用spring boot 的fat jar 插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
<mainClass>com.dalongplugin.bootstrap.BootstrapApplication</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 入口代码
package com.dalongplugin.bootstrap;
import com.dalongpluginb.PluginContract;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.plugin.core.config.EnablePluginRegistries;
// 通过scanBasePackages 配置需要进行的包扫描,间接解决插件加载的问题
@SpringBootApplication(scanBasePackages = {"com.dalongpluginb","com.dalongplugin.bootstrap","com.dalongplugina"})
// 进行插件扫描
@EnablePluginRegistries(PluginContract.class)
public class BootstrapApplication {
public static void main(String[] args) {
SpringApplication.run(BootstrapApplication.class, args);
}
// ApplicationRunner 启动之后进行插件的调用,主要是测试
@Bean
public ApplicationRunner runner(PluginRegistry<PluginContract,String> plugins) {
return args -> {
plugins.forEach(plugin -> {
System.out.println(plugin.message("hello"));
});
};
}
}
使用效果
因为默认本地开发插件都加载了(maven 配置了),因为我们使用的是通过外部classpath 模式,所以可以随时进行插件的添加以及移除
- 构建
mvn clean package
- bootstrap 效果
├── bootstrap-0.0.1-SNAPSHOT.jar
└── libs
├── accessors-smart-1.2.jar
├── android-json-0.0.20131108.vaadin1.jar
├── apiguardian-api-1.1.0.jar
├── asm-5.0.4.jar
├── assertj-core-3.18.1.jar
├── byte-buddy-1.10.19.jar
├── byte-buddy-agent-1.10.19.jar
├── hamcrest-2.2.jar
├── jackson-annotations-2.11.4.jar
├── jackson-core-2.11.4.jar
├── jackson-databind-2.11.4.jar
├── jackson-datatype-jdk8-2.11.4.jar
├── jackson-datatype-jsr310-2.11.4.jar
├── jackson-module-parameter-names-2.11.4.jar
├── jakarta.activation-api-1.2.2.jar
├── jakarta.annotation-api-1.3.5.jar
├── jakarta.el-3.0.3.jar
├── jakarta.xml.bind-api-2.3.3.jar
├── json-path-2.4.0.jar
├── json-smart-2.3.jar
├── jsonassert-1.5.0.jar
├── jul-to-slf4j-1.7.30.jar
├── junit-jupiter-5.7.0.jar
├── junit-jupiter-api-5.7.0.jar
├── junit-jupiter-engine-5.7.0.jar
├── junit-jupiter-params-5.7.0.jar
├── junit-platform-commons-1.7.0.jar
├── junit-platform-engine-1.7.0.jar
├── log4j-api-2.13.3.jar
├── log4j-to-slf4j-2.13.3.jar
├── logback-classic-1.2.3.jar
├── logback-core-1.2.3.jar
├── mockito-core-3.6.28.jar
├── mockito-junit-jupiter-3.6.28.jar
├── objenesis-3.1.jar
├── opentest4j-1.2.0.jar
├── plugin_a-1.0-SNAPSHOT.jar
├── plugin_b-1.0-SNAPSHOT.jar
├── plugin_interface-1.0-SNAPSHOT.jar
├── slf4j-api-1.7.30.jar
├── snakeyaml-1.27.jar
├── spring-aop-5.3.3.jar
├── spring-beans-5.3.3.jar
├── spring-boot-2.4.2.jar
├── spring-boot-autoconfigure-2.4.2.jar
├── spring-boot-starter-2.4.2.jar
├── spring-boot-starter-json-2.4.2.jar
├── spring-boot-starter-logging-2.4.2.jar
├── spring-boot-starter-test-2.4.2.jar
├── spring-boot-starter-tomcat-2.4.2.jar
├── spring-boot-starter-web-2.4.2.jar
├── spring-boot-test-2.4.2.jar
├── spring-boot-test-autoconfigure-2.4.2.jar
├── spring-context-5.3.3.jar
├── spring-core-5.3.3.jar
├── spring-expression-5.3.3.jar
├── spring-jcl-5.3.3.jar
├── spring-plugin-core-2.0.0.RELEASE.jar
├── spring-plugin-metadata-2.0.0.RELEASE.jar
├── spring-test-5.3.3.jar
├── spring-web-5.3.3.jar
├── spring-webmvc-5.3.3.jar
├── tomcat-embed-core-9.0.41.jar
├── tomcat-embed-websocket-9.0.41.jar
└── xmlunit-core-2.7.0.jar
默认运行效果
移除一个插件的效果
说明
实际上目前也是有不少缺陷的,比如类java,外部jar 加载,以及类隔离的一些问题,同时也缺少维护了,但是对于简单项目还是很不错的,可以使用
参考资料
https://github.com/spring-projects/spring-plugin
https://github.com/rongfengliang/spring-plugin-learning