SpringPlugin-Core在業務中的應用


前言

一直負責部門的訂單模塊,從php轉到Java也是如此,換了一種語言來實現訂單相關功能。那么Spring里有很多已經搭建好基礎模塊的設計模式來幫助我們解耦實際業務中的邏輯,用起來非常的方便!就比如我們的訂單操作模塊。生成訂單后,有很多操作。比如:取消、支付、關閉....等等。那么用設計模式的思想去處理這些不同的操作,最好用的就是策略模式來解決它們!把不同的操作分配到不同的實現類里去。這不,我發現了一個不錯的東西Spring plugin!

Spring Plugin

Spring plugin這個小東西我也是在看一些開源項目才看到的,感覺還不錯。就立馬拿來用了下,把它帶到我們業務場景里去。這不,帶大家體驗下。

下面用Spring plugin來重構下訂單的相關操作實現。這里我們只模擬,支付和關閉的操作。最后再來簡單分析下Spring plugin的原理

實戰

首先呢、定義一個操作類型的枚舉類,來邊界下當前我們系統支持的操作類型!

public enum OrderOperatorType {
    /**
     * 關閉
     */
    CLOSED,

    /**
     * 支付
     */
    PAY
    ;
}

第二步、定義操作接口,實現Spring pluginPlugin<S>接口,和配置插件

public interface OrderOperatorPlugin extends Plugin<OrderOperatorDTO> {

    /**
     * 定義操作動作
     * @param operator
     * @return
     */
    public Optional<?> apply(OrderOperatorDTO operator);
}

//配置插件,插件寫好了,我們要讓插件生效!
@Configuration
@EnablePluginRegistries({OrderOperatorPlugin.class})
public class OrderPluginConfig {
}

第三步 、定義具體的實現類(支付操作、關閉操作)

@Component
public class PayOperator implements OrderOperatorPlugin {

    @Override
    public Optional<?> apply(OrderOperatorDTO operator) {
        //支付操作
        //doPay()
        return Optional.of("支付成功");
    }

    @Override
    public boolean supports(OrderOperatorDTO operatorDTO) {
        return operatorDTO.getOperatorType() == OrderOperatorType.PAY;
    }
}



@Component
public class ClosedOperator implements OrderOperatorPlugin {

    @Override
    public Optional<?> apply(OrderOperatorDTO operator) {
        //關閉操作
        //doClosed()
        return Optional.of("關閉訂單成功");
    }

    @Override
    public boolean supports(OrderOperatorDTO operatorDTO) {
        return operatorDTO.getOperatorType() == OrderOperatorType.CLOSED;
    }
}

這里要注意的是實現 supports方法,此方法返回的是一個boolean值,直觀的看起來就是一個選擇器的條件,這里可直接認為,當Support返回True的時候,就找到了當前操作的實現類!

兩個不同的實現類定義好,那么我們怎么找到具體的實現類呢?

最后、定義業務接口,和業務實現類

public interface OrderService {

    /**
     * 操作訂單接口
     * @param operator
     * @return
     */
    Optional<?> operationOrder(OrderOperatorDTO operator);
}


@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    PluginRegistry<OrderOperatorPlugin, OrderOperatorDTO> orderOperatorPluginRegistry;

    @Override
    public Optional<?> operationOrder(OrderOperatorDTO operator) {
        OrderOperatorPlugin pluginFor = orderOperatorPluginRegistry.getPluginFor(operator);
        return pluginFor.apply(operator);
    }
}

在業務接口實現類里我們注入了

@Resource
PluginRegistry<OrderOperatorPlugin, OrderOperatorDTO> orderOperatorPluginRegistry;

名字一定是 接口名 + Registry,我這里是orderOperatorPluginRegistry至於為什么要這樣寫,等回我們分析源碼的時候看一下。目前這樣寫就對了。

接下來我們測試下

@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderOperatorPluginTest {

    @Resource
    OrderService orderService;

    @Resource
    ApplicationContext applicationContext;

    @Test
    public void test_operation_closed() {
        final OrderOperatorDTO operator = new OrderOperatorDTO();
        operator.setOperatorType(OrderOperatorType.CLOSED);
        Optional<?> optionalO = orderService.operationOrder(operator);

        Assertions.assertEquals("關閉訂單成功", optionalO.get());
    }


    @Test
    public void test_operation_pay() {
        final OrderOperatorDTO operator = new OrderOperatorDTO();
        operator.setOperatorType(OrderOperatorType.PAY);
        Optional<?> optionalO = orderService.operationOrder(operator);

        Assertions.assertEquals("支付成功", optionalO.get());
    }
}

這個運行結果是沒有問題的,可以自己把代碼下載下來,跑一下~~🤪🤪

感受

如果我把整個訂單的流程都當作不同的插件來開發的話...創建訂單是一個流程、在這個流程的過程中,我們分別在不同的位置插入不同的插件。比如下圖!


最后,這只把所以Plugin組織起來,是不是就可以搞定一套完整的訂單流程了,而我們做的只是面對插件開發,把注意力集中到某個插件中就可以了呢?或許下次訂單重構的時候,我可以會這樣的去嘗試下!

源碼重點分析

首先看下注冊插件的注釋EnablePluginRegistries

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import({PluginRegistriesBeanDefinitionRegistrar.class})
public @interface EnablePluginRegistries {
    Class<? extends Plugin<?>>[] value();
}

value屬性是一個數組,必須實現Plugin接口,這個是定義插件的基本條件~。

再看Import注釋,PluginRegistriesBeanDefinitionRegistrar實現了ImportBeanDefinitionRegistrar接口,這個有點味道了,

public class PluginRegistriesBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  
}

之前我有一個文章是分析相關加載類到容器的一篇文章,請看下面文章的介紹。

ImportBeanDefinitionRegistrar的作用

直接看核心代碼

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  			//當前我只注冊了一個 插件 OrderOperatorPlugin
        Class<?>[] types = (Class[])((Class[])importingClassMetadata.getAnnotationAttributes(EnablePluginRegistries.class.getName()).get("value"));
        Class[] var4 = types;
        int var5 = types.length;
				//長度也就為1
        for(int var6 = 0; var6 < var5; ++var6) {
            Class<?> type = var4[var6];
          	//是FactoryBean 見名思義。PluginRegistryFactoryBean#getObject 的方法最終返回的是 OrderAwarePluginRegistry 看名字是支持排序的功能。
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PluginRegistryFactoryBean.class);
            builder.addPropertyValue("type", type);
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            Qualifier annotation = (Qualifier)type.getAnnotation(Qualifier.class);
            if (annotation != null) {
                AutowireCandidateQualifier qualifierMetadata = new AutowireCandidateQualifier(Qualifier.class);
                qualifierMetadata.setAttribute(AutowireCandidateQualifier.VALUE_KEY, annotation.value());
                beanDefinition.addQualifier(qualifierMetadata);
            }
						
          	//插件上沒有添加 Qualifier 所以為null, nulll的話就給拼接上 Registry! 這就是為啥注入的時候用 插件名 + Registry、另外 PluginRegistryFactoryBean實現了PluginRegistry。
            String beanName = annotation == null ? StringUtils.uncapitalize(type.getSimpleName() + "Registry") : annotation.value();
            registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
        }

    }

那么注入容器后,調用getPluginFor找到當前策略的實現類。

// OrderAwarePluginRegistry 類
public T getPluginFor(S delimiter) {
  Iterator var2 = super.getPlugins().iterator();

  Plugin plugin;
  do {
    if (!var2.hasNext()) {
      return null;
    }

    plugin = (Plugin)var2.next();
    //這里判斷 supports的方法 返回true時即跳出Loop
  } while(plugin == null || !plugin.supports(delimiter));

  return plugin;
}


//另外 super.getPlugins里 會調用 initializa的方法,即插件是支持排序功能的,只要在插件上加入Order()注釋即可。
protected List<T> initialize(List<T> plugins) {
  List<T> result = super.initialize(plugins);
  Collections.sort(result, this.comparator);
  return result;
}

代碼在GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM