前言
一直负责部门的订单模块,从php转到Java也是如此,换了一种语言来实现订单相关功能。那么Spring里有很多已经搭建好基础模块的设计模式来帮助我们解耦实际业务中的逻辑,用起来非常的方便!就比如我们的订单操作模块。生成订单后,有很多操作。比如:取消、支付、关闭....等等。那么用设计模式的思想去处理这些不同的操作,最好用的就是策略模式来解决它们!把不同的操作分配到不同的实现类里去。这不,我发现了一个不错的东西Spring plugin
!
Spring Plugin
Spring plugin
这个小东西我也是在看一些开源项目才看到的,感觉还不错。就立马拿来用了下,把它带到我们业务场景里去。这不,带大家体验下。
下面用Spring plugin
来重构下订单的相关操作实现。这里我们只模拟,支付和关闭的操作。最后再来简单分析下Spring plugin
的原理
实战
首先呢、定义一个操作类型的枚举类,来边界下当前我们系统支持的操作类型!
publicenumOrderOperatorType{/***关闭*/CLOSED,/***支付*/PAY;}
第二步、定义操作接口,实现Spring plugin
的Plugin<S>
接口,和配置插件
publicinterfaceOrderOperatorPluginextendsPlugin<OrderOperatorDTO>{/***定义操作动作*@paramoperator*@return*/publicOptional<?>apply(OrderOperatorDTOoperator);}//配置插件,插件写好了,我们要让插件生效!@Configuration@EnablePluginRegistries({OrderOperatorPlugin.class})publicclassOrderPluginConfig{}
第三步 、定义具体的实现类(支付操作、关闭操作)
@ComponentpublicclassPayOperatorimplementsOrderOperatorPlugin{@OverridepublicOptional<?>apply(OrderOperatorDTOoperator){//支付操作//doPay()returnOptional.of("支付成功");}@Overridepublicbooleansupports(OrderOperatorDTOoperatorDTO){returnoperatorDTO.getOperatorType()==OrderOperatorType.PAY;}}@ComponentpublicclassClosedOperatorimplementsOrderOperatorPlugin{@OverridepublicOptional<?>apply(OrderOperatorDTOoperator){//关闭操作//doClosed()returnOptional.of("关闭订单成功");}@Overridepublicbooleansupports(OrderOperatorDTOoperatorDTO){returnoperatorDTO.getOperatorType()==OrderOperatorType.CLOSED;}}
这里要注意的是实现 supports
方法,此方法返回的是一个boolean
值,直观的看起来就是一个选择器的条件,这里可直接认为,当Support
返回True
的时候,就找到了当前操作的实现类!
两个不同的实现类定义好,那么我们怎么找到具体的实现类呢?
最后、定义业务接口,和业务实现类
publicinterfaceOrderService{/***操作订单接口*@paramoperator*@return*/Optional<?>operationOrder(OrderOperatorDTOoperator);}@ServicepublicclassOrderServiceImplimplementsOrderService{@ResourcePluginRegistry<OrderOperatorPlugin,OrderOperatorDTO>orderOperatorPluginRegistry;@OverridepublicOptional<?>operationOrder(OrderOperatorDTOoperator){OrderOperatorPluginpluginFor=orderOperatorPluginRegistry.getPluginFor(operator);returnpluginFor.apply(operator);}}
在业务接口实现类里我们注入了
@ResourcePluginRegistry<OrderOperatorPlugin,OrderOperatorDTO>orderOperatorPluginRegistry;
名字一定是 接口名 + Registry,我这里是orderOperatorPluginRegistry
至于为什么要这样写,等回我们分析源码的时候看一下。目前这样写就对了。
接下来我们测试下
@RunWith(SpringRunner.class)@SpringBootTestpublicclassOrderOperatorPluginTest{@ResourceOrderServiceorderService;@ResourceApplicationContextapplicationContext;@Testpublicvoidtest_operation_closed(){finalOrderOperatorDTOoperator=newOrderOperatorDTO();operator.setOperatorType(OrderOperatorType.CLOSED);Optional<?>optionalO=orderService.operationOrder(operator);Assertions.assertEquals("关闭订单成功",optionalO.get());}@Testpublicvoidtest_operation_pay(){finalOrderOperatorDTOoperator=newOrderOperatorDTO();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@interfaceEnablePluginRegistries{Class<?extendsPlugin<?>>[]value();}
value
属性是一个数组,必须实现Plugin
接口,这个是定义插件的基本条件~。
再看Import
注释,PluginRegistriesBeanDefinitionRegistrar
实现了ImportBeanDefinitionRegistrar
接口,这个有点味道了,
publicclassPluginRegistriesBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{}
之前我有一个文章是分析相关加载类到容器的一篇文章,请看下面文章的介绍。
ImportBeanDefinitionRegistrar的作用
直接看核心代码
publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){//当前我只注册了一个插件OrderOperatorPluginClass<?>[]types=(Class[])((Class[])importingClassMetadata.getAnnotationAttributes(EnablePluginRegistries.class.getName()).get("value"));Class[]var4=types;intvar5=types.length;//长度也就为1for(intvar6=0;var6<var5;++var6){Class<?>type=var4[var6];//是FactoryBean见名思义。PluginRegistryFactoryBean#getObject的方法最终返回的是OrderAwarePluginRegistry看名字是支持排序的功能。BeanDefinitionBuilderbuilder=BeanDefinitionBuilder.rootBeanDefinition(PluginRegistryFactoryBean.class);builder.addPropertyValue("type",type);AbstractBeanDefinitionbeanDefinition=builder.getBeanDefinition();Qualifierannotation=(Qualifier)type.getAnnotation(Qualifier.class);if(annotation!=null){AutowireCandidateQualifierqualifierMetadata=newAutowireCandidateQualifier(Qualifier.class);qualifierMetadata.setAttribute(AutowireCandidateQualifier.VALUE_KEY,annotation.value());beanDefinition.addQualifier(qualifierMetadata);}//插件上没有添加Qualifier所以为null,nulll的话就给拼接上Registry!这就是为啥注入的时候用插件名+Registry、另外PluginRegistryFactoryBean实现了PluginRegistry。StringbeanName=annotation==null?StringUtils.uncapitalize(type.getSimpleName()+"Registry"):annotation.value();registry.registerBeanDefinition(beanName,builder.getBeanDefinition());}}
那么注入容器后,调用getPluginFor
找到当前策略的实现类。
//OrderAwarePluginRegistry类publicTgetPluginFor(Sdelimiter){Iteratorvar2=super.getPlugins().iterator();Pluginplugin;do{if(!var2.hasNext()){returnnull;}plugin=(Plugin)var2.next();//这里判断supports的方法返回true时即跳出Loop}while(plugin==null||!plugin.supports(delimiter));returnplugin;}//另外super.getPlugins里会调用initializa的方法,即插件是支持排序功能的,只要在插件上加入Order()注释即可。protectedList<T>initialize(List<T>plugins){List<T>result=super.initialize(plugins);Collections.sort(result,this.comparator);returnresult;}
代码在GitHub