Spring Plugin与策略模式:打造动态可扩展的应用

news/2024/11/15 17:33:19/

目录

一、策略模式

Spring%20Plugin-toc" style="margin-left:0px;">二、Spring Plugin

Spring%20Plugin%20%E5%AE%9E%E7%8E%B0%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E5%BC%80%E5%8F%91-toc" style="margin-left:40px;">        2.1 Spring Plugin 实现策略模式开发

        2.2 策略模式优缺点

Spring%20Plugin%20%E5%8E%9F%E7%90%86-toc" style="margin-left:0px;">三、Spring Plugin 原理


一、策略模式

        策略模式是一种设计模式,它允许程序在运行中动态的选择不同的行为方式进行动态执行。策略模式的核心思想是将行为封装在一个个独立的类中,这些类实现了相同的接口或抽象类,客户端可以通过接口来调用不同的实现,而不知道具体的实现细节。下面来看一个具体的案例。

        现在的移动支付非常的便捷,而且有很多支付方式,假如让你负责支付路由的设计该如何设计,如何实现支付渠道的选择的呢?

        比如用户支付时可以选择支付宝、微信、银行卡,那系统底层是如何进行操作的,后期如果在加入新的支付方式,该如何进行扩展呢?

        当然如果你使用 if else 肯定是能实现的,但这种代码可读性差、可维护性差,而且不利于扩展,使用策略模式就能优雅的解决这些问题。

Spring%20Plugin">二、Spring Plugin

        Spring Plugin 是 Spring 框架的一个扩展,用于实现插件化开发。它提供了插件注册、加载、卸载等功能。Spring Plugin 提供了一种简单而有效的方式来实现插件化开发,使得应用程序能够更加灵活和易于维护。

        下面通过 Spring Plugin 来实现上面提到的支付路由的策略模式

Spring%20Plugin%20%E5%AE%9E%E7%8E%B0%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E5%BC%80%E5%8F%91">        2.1 Spring Plugin 实现策略模式开发

        引入依赖

<dependency><groupId>org.springframework.plugin</groupId><artifactId>spring-plugin-core</artifactId><version>指定版本</version>
</dependency>

        定义支付方式接口

public interface PaymentStrategy extends Plugin<String> {/*** 支付路由选择** @param paymentReq 待处理的订单信息, 入参中携带支付标识* @return*/PayResult pay(PaymentReq paymentReq);
}

        具体的支付实现

// 支付宝支付实现
@Service
public class AliPayService implements PaymentStrategy {@Overridepublic PayResult pay(PaymentReq paymentReq) {// 模拟支付宝支付流程return new PayResult();}@Overridepublic boolean supports(String payment) {// 支付方式是否为支付宝,这里简化一些,正常情况下需要使用枚举return "alipay".equals(payment);}
}// 微信支付实现
@Service
public class WechatPayService implements PaymentStrategy {@Overridepublic PayResult pay(PaymentReq paymentReq) {// 模拟微信支付流程return new PayResult();}@Overridepublic boolean supports(String payment) {// 支付方式是否为微信,这里简化一些,正常情况下需要使用枚举return "wechatpay".equals(payment);}
}

        假如后期要加入银联支付方式,相信你一定知道如何实现了吧。

        定义插件配置

@Configuration
@EnablePluginRegistries({PaymentStrategy.class})
public class StrategyConfig {}

        使用支付方式进行支付操作

@RestController
public class PaymentController {@Autowiredprivate PluginRegistry<PaymentStrategy, String> registry;@PostMapping(value = "/pay")public PayResult pay(PaymentReq req) {PaymentStrategy strategy = registry.getRequiredPluginFor(req.getPaymentType());return strategy.pay(req);}
}

        上述即时使用 Spring Plugin 实现策略模式的案例,是不是很简单呢。

        2.2 策略模式优缺点

        策略模式的优点很明显,有以下优点

  1. 扩展性:使用策略模式时,如果要添加新的策略十分方便也很简单,不用修改原有的代码,扩展性好。
  2. 解耦:客户端调用时只需要知道策略接口,而具体的实现不必担心。
  3. 动态性:可以在运行时动态进行不同策略的切换,提高了灵活性和适应性。

        但是也有一定的缺点,为了实现每个策略类,都需要一个新的类进行独立的封装,增加了复杂性。但是与其扩展性来说,这点实际上是可以忽略的。

Spring%20Plugin%20%E5%8E%9F%E7%90%86">三、Spring Plugin 原理

        开启 Spring Plugin 功能的入口是 @EnablePluginRegistries 注解,先看一下其实现。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(PluginRegistriesBeanDefinitionRegistrar.class)
public @interface EnablePluginRegistries {/*** The {@link Plugin} types to register {@link PluginRegistry} instances for. The registries will be named after the* uncapitalized plugin type extended with {@code Registry}. So for a plugin interface {@code SamplePlugin} the* exposed bean name will be {@code samplePluginRegistry}. This can be used on the client side to make sure you get* the right {@link PluginRegistry} injected by using the {@link Qualifier} annotation and referring to that bean* name. If the auto-generated bean name collides with one already in your application you can use the* {@link Qualifier} annotation right at the plugin interface to define a custom name.* * @return*/Class<? extends Plugin<?>>[] value();}

        该注解声明了需要开启插件化能力的接口,并且导入了PluginRegistriesBeanDefinitionRegistrar,它是一个 ImportBeanDefinitionRegistrar,会在 Spring Boot 启动的时候执行 registerBeanDefinitions 方法。registerBeanDefinitions 方法实现如下:

@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnablePluginRegistries.class.getName());if (annotationAttributes == null) {LOG.info("No EnablePluginRegistries annotation found on type {}!", importingClassMetadata.getClassName());return;}Class<?>[] types = (Class<?>[]) annotationAttributes.get("value");for (Class<?> type : types) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PluginRegistryFactoryBean.class);builder.addPropertyValue("type", type);RootBeanDefinition beanDefinition = (RootBeanDefinition) builder.getBeanDefinition();beanDefinition.setTargetType(getTargetType(type));Qualifier annotation = type.getAnnotation(Qualifier.class);// If the plugin interface has a Qualifier annotation, propagate that to the bean definition of the registryif (annotation != null) {AutowireCandidateQualifier qualifierMetadata = new AutowireCandidateQualifier(Qualifier.class);qualifierMetadata.setAttribute(AutowireCandidateQualifier.VALUE_KEY, annotation.value());beanDefinition.addQualifier(qualifierMetadata);}// DefaultString beanName = annotation == null //? StringUtils.uncapitalize(type.getSimpleName() + "Registry") //: annotation.value();registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}}

        registerBeanDefinitions 从 EnablePluginRegistries 解析出插件接口,然后注册成     PluginRegistryFactoryBean 类型的 BeanDefination。

        PluginRegistryFactoryBean 是一个 FactoryBean,所以注入 PluginRegistry 类型的时候实际是调用 PluginRegistryFactoryBean 的 getObject 返回的内容。

        

public class PluginRegistryFactoryBean<T extends Plugin<S>, S> extends AbstractTypeAwareSupport<T>implements FactoryBean<PluginRegistry<T, S>> {@NonNullpublic OrderAwarePluginRegistry<T, S> getObject() {return OrderAwarePluginRegistry.of(getBeans());}@NonNullpublic Class<?> getObjectType() {return OrderAwarePluginRegistry.class;}public boolean isSingleton() {return true;}
}

        注入的时候返回的类型是 OrderAwarePluginRegistry,注入调用 getObject 返回,里边调用了父类 AbstractTypeAwareSupport 的 getBeans 方法。

protected List<T> getBeans() {TargetSource targetSource = this.targetSource;if (targetSource == null) {throw new IllegalStateException("Traget source not initialized!");}ProxyFactory factory = new ProxyFactory(List.class, targetSource);return (List<T>) factory.getProxy();
}public void afterPropertiesSet() {ApplicationContext context = this.context;if (context == null) {throw new IllegalStateException("ApplicationContext not set!");}Class<?> type = this.type;if (type == null) {throw new IllegalStateException("No type configured!");}this.targetSource = new BeansOfTypeTargetSource(context, type, false, exclusions);
}

        由于实现了 InitializingBean 接口,初始化时会获取到 ApplicationContext 上下文,基于上下文的 type 封装成 BeansOfTypeTargetSource 赋值给 targetSource 变量,BeansOfTypeTargetSource 实现了 TargetSource,getTarget返回基于实际类型封装的增强类型。

class BeansOfTypeTargetSource implements TargetSource {@NonNull@SuppressWarnings({ "rawtypes", "unchecked" })public synchronized Object getTarget() throws Exception {Collection<Object> components = this.components == null //? getBeansOfTypeExcept(type, exclusions) //: this.components;if (frozen && this.components == null) {this.components = components;}return new ArrayList(components);}private Collection<Object> getBeansOfTypeExcept(Class<?> type, Collection<Class<?>> exceptions) {return Arrays.stream(context.getBeanNamesForType(type, false, eagerInit)) //.filter(it -> !exceptions.contains(context.getType(it))) //.map(it -> context.getBean(it)) //.collect(Collectors.toList());}
}

           getBeans 方法,会基于动态代理将 BeansOfTypeTargetSource 创建成 List 类型代理对象备用。然后回到 PluginRegistryFactoryBean 的 getObject 方法,会最终将插件接口实现封装成OrderAwarePluginRegistry 类型。

        也就是说通过 PluginRegistryFactoryBean 注入的 PluginRegistry 是包含了所有实现了插件接口实例的封装类型,我们常用到的有 getPlugins 和 getPluginFor 方法:

@Override
public List<T> getPlugins() {return Collections.unmodifiableList(super.getPlugins());
}@Override
public Optional<T> getPluginFor(S delimiter) {return super.getPlugins().stream()//.filter(it -> it.supports(delimiter))//.findFirst();
}

        到这里基本上就可以了解其工作原理了。

往期经典推荐:

从新手到高手:Spring AOP的进阶指南_springaop切面优先级-CSDN博客

Sentinel与Nacos强强联合,构建微服务稳定性基石的重要实践_sentinel nacos-CSDN博客

从0开始理解云原生架构_云原生发展历史-CSDN博客

TiDB高手进阶:揭秘自增ID热点现象与高级调优技巧_tidb 自增id-CSDN博客

SpringBoot项目并发处理大揭秘,你知道它到底能应对多少请求洪峰?_一个springboot能支持多少并发-CSDN博客


http://www.ppmy.cn/news/1547244.html

相关文章

表的数据结构和常见操作

在计算机科学中&#xff0c;表数据结构是一种用于组织和存储数据的方式&#xff0c;它具有行和列的形式&#xff0c;类似于电子表格或数据库表。表数据结构可以用于多种用途&#xff0c;具体取决于实现和使用场景。以下是几种常见的表数据结构&#xff1a; ### 1. 数组&#x…

Linux Kernel Programming 2

目录 书写内核框架 起手我们需要理解的是&#xff1a;用户态和内核态 库和系统调用 API 内核空间组件 探索 LKM&#xff08;Linux Kernel Module体系&#xff09; LKM 框架 内核源代码树中的内核模块 modinfo 动手&#xff01;写年轻人的第一个内核模块程序 先试试看&…

NFS-Ganesha 核心架构解读

NFSv4 简要概述 NFS 这个协议( NFSv2 )最初由 Sun Microsystems 在 1984 年设计提出&#xff0c;由于存在一些不足&#xff0c;因此在随后由几家公司联合推出了 NFSv3。到了 NFSv4 时&#xff0c;开发完全由 IETF 主导&#xff0c;设计目标是&#xff1a; 提高互联下的 NFS 访…

to_sql报错not all arguments converted during string formatting

报错&#xff1a; DatabaseError: Execution failed on sql SELECT name FROM sqlite_master WHERE typetable AND name?;: not all arguments converted during string formattingb 报错的代码如下&#xff1a; import pymysql import pandas as pd con pymysql.connect(…

c文件的编译,汇编,基础知识

文章目录 前言一、预编译二、编译阶段三、汇编1&#xff0c; objdump的用法2&#xff0c;objdump 举例objdump -s hello.o 输出&#xff08;节内容&#xff09; 四&#xff0c;代码段 前言 #include <stdio.h> int main(){printf("hello, world\n");}从这个最…

矩阵的对角化特征值分解

矩阵对角化和特征值分解实际上描述的是同一个过程的不同方面。矩阵对角化 强调的是通过相似变换将矩阵 A A A转化为对角矩阵 D D D。特征值分解 强调的是如何通过矩阵的特征值和特征向量来实现这种对角化。 矩阵对角化 矩阵对角化是指将一个方阵 A A A通过相似变换转化为一个…

android webview常见内容

WebView 是 Android 系统中用于展示网页内容的一个组件。 Android 4.4 之前&#xff0c; 使用 WebKit 渲染引擎&#xff0c;之后使用了 Chromium 的内核 url 加载流程 首先&#xff0c;通过loadUrl()方法或者loadData()等方法来触发加载。当调用这些方法后&#xff0c;WebVie…

《目标检测》R-CNN网络基础(RCNN,Fast-RCNN)

文章目录 1.Overfeat模型2.RCNN网络2.1 算法流程2.1.1 候选区域的生成&#xff08;了解&#xff0c;已经不再使用了&#xff09;2.1.2 CNN网络提取特征2.1.3 目标分类&#xff08;SVM&#xff09;2.1.4 目标回归&#xff08;线性回归修正坐标&#xff09;2.1.5 预测过程 2.2 算…