关于spring:SpringPluginCore在业务中的应用

前言

始终负责部门的订单模块,从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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理