优雅!Spring 基于 Plugin 插件开发(官方推荐)

文摘   2025-01-14 08:02   新疆  

Spring Boot 3实战案例合集》现已囊括超过80篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。

【重磅发布】《Spring Boot 3实战案例锦集》PDF电子书现已出炉!

🎉🎉我们精心打造的《Spring Boot 3实战案例锦集》PDF电子书现已正式完成,目前已经有80个案例,后续还将继续更新。文末有电子书目录。

💪💪永久更新承诺


我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。这意味着,随着技术的不断发展和Spring Boot 3的深入应用,我们的电子书也将持续更新,确保您始终掌握最前沿、最实用的技术知识。

🔥🔥精彩内容不容错过
《Spring Boot 3实战案例锦集》汇聚了众多精心挑选的实战案例,旨在帮助您快速掌握Spring Boot 3的核心技术和实战技巧。无论您是初学者还是有一定经验的开发者,都能从中受益匪浅。

💌💌如何获取
请立即订阅我们的合集点我订阅,并通过私信联系我们,我们将第一时间将电子书发送给您。

现在就订阅合集




环境:SpringBoot3.2.5



1. 简介

构建可扩展架构是创建可维护应用程序的核心原则。这正是像OSGi这样的功能齐全的插件环境如此受欢迎的原因。然而,不幸的是,引入OSGi会给项目带来许多复杂性。

Spring Plugin通过提供插件实现来扩展核心系统功能的核心灵活性,从而为插件开发提供了一种更为实用的方法。当然,它并不提供OSGi的核心功能,如动态类加载或插件的运行时安装和部署。尽管因此Spring Plugin远不如OSGi强大,但它却能满足构建模块化可扩展应用程序的基本需求。

Spring Plugin紧密集成到Spring的组件模型中,并通过一些自定义功能扩展了核心容器。

接下来,我们将结合如下的示例进行讲解:

一个支付系统需要支持多种支付方式,包括微信、支付宝、银行卡等。为了满足这一需求,我们希望能够根据特定条件在这些支付方式之间动态切换。

为了实现这一目标,我们将采用Spring Plugin来实现策略模式。在Spring Plugin中,有一个为系统中所有插件设计的中心接口。这个接口的目的是为了被具体的插件接口所扩展。Plugin接口的主要职责是定义一个决策对象,这样具体的实现就可以利用这个对象来判断它们是否支持针对特定决策对象的执行。

2. 实战案例

准备环境

<dependency>  <groupId>org.springframework.plugin</groupId>  <artifactId>spring-plugin-core</artifactId>  <version>3.0.0</version></dependency><dependency>  <groupId>org.springframework.plugin</groupId>  <artifactId>spring-plugin-metadata</artifactId>  <version>3.0.0</version></dependency>

只需要引入上面2个依赖包。

2.1 定义支付接口

public interface PaymentProcessingService extends Plugin<PaymentMethod> {    String processPayment(BigDecimal amount);}

Plugin源码

public interface Plugin<S> {
  // 仅仅定义了一个supports方法;该方法会用来判断当前是否支持的方式 boolean supports(S delimiter);}

支付类型定义

public enum PaymentMethod {
WX, ALIPAY, CARD}

接口定义完后,接下来定义具体支付的实现。

2.2 支付实现

微信(WX)支付实现

@Componentpublic class WXProcessingService implements PaymentProcessingService {
@Override public String processPayment(BigDecimal amount) { return String.format("微信支付完成: %s", amount) ; }
@Override public boolean supports(PaymentMethod delimiter) { return delimiter.equals(PaymentMethod.WX) ; }}

在该实现中我们需要重写 Plugin<S> 接口中的 boolean supports(S delimiter); 方法,以便这个特定的实现支持 PaymentMethod.WX 支付方式。

其它支付方式实现

// 其它支付方式与上面的微信一样,仅仅是supports不一样public boolean supports(PaymentMethod delimiter) {  return delimiter.equals(PaymentMethod.ALIPAY) ;}// 银行卡支付public boolean supports(PaymentMethod delimiter) {  return delimiter.equals(PaymentMethod.CARD) ;}

以上完成了基本的实现了。

2.3 配置插件

为了使Spring Plugin能够注册这些bean,并在我们需要动态更改实现并注入正确的bean时为我们提供它们,我们在Spring应用类上添加了以下注解:

@Configuration@EnablePluginRegistries(PaymentProcessingService.class)public class PaymentPluginConfig {
}

说明:该注解的value是数组,你可以写多个Plugin类型的接口。

全局异常处理

@RestControllerAdvicepublic class GlobalExceptionAdvice {
@ExceptionHandler(NoSupportPaymentException.class) public ResponseEntity<Object> handlePaymentException(NoSupportPaymentException e) { return ResponseEntity.ok(e.getMessage()) ; }}

对于不支持的支付方式进行处理。

2.4 测试

上面对每种支付方式都实现了并且也开启了插件注册功能,那么我们该如何去使用呢?

@EnablePluginRegistries注解会注册一个FactoryBean<T>,而这里的T类型是:PluginRegistry。我们可以直接注入该对象即可。

@RestController@RequestMapping("/payment")public class PaymentController {
private final PluginRegistry<PaymentProcessingService, PaymentMethod> pluginRegistry ; public PaymentController(PluginRegistry<PaymentProcessingService, PaymentMethod> pluginRegistry) { this.pluginRegistry = pluginRegistry; }
@PostMapping public ResponseEntity<String> pay(@RequestBody PaymentRequest paymentRequest) { PaymentProcessingService paymentProcessingService = pluginRegistry.getPluginFor(paymentRequest.getPaymentMethod()) .orElseThrow(() -> new NoSupportPaymentException("不支持的支付方式")) ;
return ResponseEntity.ok(paymentProcessingService.processPayment(paymentRequest.getAmount())); }}

下面,我们将CARD支付方式上的@Component注解去掉。这样我们的系统就不支持该种支付方式了,测试结果如下:

是不是非常的优雅?

是否可以联想到Spring MVC中哪里应用到此种模式?如果你读过Spring MVC源码,那么你对HandlerAdapter一定非常了解,如下:

public interface HandlerAdapter {
boolean supports(Object handler); ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;}

当接收到一个请求后,核心DispatcherServlet会通过如下方式进行:

而我们Spring Plugin内部也是如此,上面的PluginRegistry#getPluginFor方法

3. 其它功能

3.1 控制顺序

我们可以对具体实现使用@Order或者实现Ordered接口,来控制它们的顺序,如下示例:

@Component@Order(1)public class WXProcessingService implements PaymentProcessingService {}
@Component@Order(0)public class WX2ProcessingService implements PaymentProcessingService {}

我们针对WX支付定义了2种实现,在执行时WX2ProcessingService将会生效@Order配置的值越小优先级越高。

3.2 插件元数据

元数据模块实际上围绕两个核心接口构建:PluginMetadata(插件元数据)和MetadataProvider(元数据提供者)。接口签名如下:

public interface PluginMetadata {  String getName();  String getVersion();}
public interface MetadataProvider { PluginMetadata getMetadata();}

修改我们可以让支付接口再实现MetadataProvider 。

public interface PaymentProcessingService extends Plugin<PaymentMethod>, MetadataProvider {    String processPayment(BigDecimal amount);  @Override  default PluginMetadata getMetadata() {    return null;  }}

如上提供了默认实现,这样我们可以在具体的实现中决定是否要重写该方法定义元数据。

@Component@Order(1)public class WXProcessingService implements PaymentProcessingService {  // ...  @Override  public PluginMetadata getMetadata() {    return new PluginMetadata() {      public String getVersion() {        return "1.0.0" ;      }      public String getName() {        return "微信支付" ;      }    } ;  }}

为了简化插件实现,Spring Plugin提供了 AbstractMetadataBasedPlugin,它使用内部元数据来实现 Plugin 的 supports(...) 方法。通过扩展这个基类,可以轻松构建以元数据作为选择标准的插件。这样,您可以将元数据存储在用户特定的配置文件中,并使用这些信息来选择给定用户的特定插件。

public abstract class AbstractMetadataBasedPlugin implements Plugin<PluginMetadata>, MetadataProvider {  public boolean supports(PluginMetadata delimiter) {    return getMetadata().equals(delimiter);  }}

基于元数据的判断。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

一文让你彻底搞定Spring Security的基本使用

SpringBoot整合RSocket实时数据通信

面试官:说说@Configuration与@Component有什么区别?

技术专家!Spring Boot 增强版 @RequestMapping 添加限流功能

手写Spring MVC核心组件,底层原理如此简单

项目亮点!Spring Boot 多线程事务一致性方案,支持JDBC,MyBatis,JPA

请一定牢记SpringBoot项目开发中的8个扩展接口

SpringBoot整合Flink CDC,实时追踪数据变动,无缝同步至Redis

紧急!Spring Boot安全漏洞

玩转Redis!非常强大的Redisson分布式集合,少写60%代码

SpringBoot3第三方接口调用10种方式,最后一种你肯定没用过

瞧瞧老外总结的SpringBoot最佳实践

SpringBoot3优雅停止/重启定时任务

基于Spring Boot 使用 JPA 优化性能的7大关键策略

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章