cola扩展点使用和设计初探

封装变化,可灵活应对程序的需求变化。

扩展点使用

步骤:

定义扩展点接口,类型可以是校验器,转换器,实体; 必须以ExtPt结尾,表示一个扩展点。

比如,我定义一个云枢的组织结构的扩展点接口,消息发送扩展点,二开扩展点,webapi的rest接口扩展点点。

定义扩展点接口

package com.authine.web.cola.domain.customer;import com.alibaba.cola.extension.ExtensionPointI;
import com.authine.web.cola.dto.domainmodel.Department;import java.util.List;/*** @author carter* create_date  2020/5/25 14:25* description     定义扩展点接口,对组织机构的某些方法。*/public interface OrganizationExtPt extends ExtensionPointI {/*** 根据corpId查询企业下所有部门** @param corpId        企业编号* @param includeDelete 是否包含删除的部门* @return 部门*/List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete);}

比如业务扩展分为钉钉,微信:

这里基于扩展理论(x,y);

即通过 业务,用例,场景得到扩展点的key, 那后扩展类就是针对实际的业务场景的业务处理代码;

钉钉场景扩展点实现

package com.authine.web.cola.domain.customer.extpt;import com.alibaba.cola.extension.Extension;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import lombok.extern.slf4j.Slf4j;import java.util.Collections;
import java.util.List;/*** @author carter* create_date  2020/5/25 14:32* description     企业部门在通过corpId获取部门列表的场景下,钉钉的扩展*/
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "dingTalk")
@Slf4j
public class DingTalkOrganizationExt implements OrganizationExtPt {@Overridepublic List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {log.info("在组织结构业务,通过企业编号获取部门列表的用例,在钉钉的场景下业务的实现处理方式");log.info("通过钉钉的配置信息和API获取得到组织信息,并组装成云枢识别的部门信息");Department department = new Department();department.setName("dingTalk");department.setCorpId(corpId);return Collections.singletonList(department);}
}

企业微信扩展点实现

package com.authine.web.cola.domain.customer.extpt;import com.alibaba.cola.extension.Extension;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import lombok.extern.slf4j.Slf4j;import java.util.Collections;
import java.util.List;/*** @author carter* create_date  2020/5/25 15:05* description     企业微信的扩展点实现*/
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "wechat")
@Slf4j
public class WechatOrganizationExt  implements OrganizationExtPt {@Overridepublic List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {log.info("业务:组织机构,用例:通过企业编号获取部门 , 场景:企业微信");log.info("通过企业微信的API获取组织的部门信息,然后包装为需要的部门列表");Department department = new Department();department.setName("wechat");department.setCorpId(corpId);return Collections.singletonList(department);}
}

扩展点使用

在命令执行器中使用。

package com.authine.web.cola.executor.query;import com.alibaba.cola.command.Command;
import com.alibaba.cola.command.CommandExecutorI;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.extension.ExtensionExecutor;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import com.authine.web.cola.dto.OrgnizationQry;import java.util.List;/*** @author carter* create_date  2020/5/25 15:09* description     查询组织机构的指令执行*/
@Command
public class OrgazationQueryExe implements CommandExecutorI<MultiResponse, OrgnizationQry> {private final ExtensionExecutor extensionExecutor;public OrgazationQueryExe(ExtensionExecutor extensionExecutor) {this.extensionExecutor = extensionExecutor;}@Overridepublic MultiResponse execute(OrgnizationQry cmd) {String corpId = cmd.getCorpId();boolean includeDelete = cmd.isIncludeDelete();List<Department> departmentList = extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(),ex -> ex.getDepartmentsByCorpId(corpId, includeDelete));return MultiResponse.ofWithoutTotal(departmentList);}
}

测试扩展点的使用

封装一个http接口来调用。

package com.authine.web.cola.controller;import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.extension.BizScenario;
import com.authine.web.cola.api.OrganizationServiceI;
import com.authine.web.cola.dto.OrgnizationQry;
import com.authine.web.cola.dto.domainmodel.Department;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OrganizationController {private final OrganizationServiceI organizationServiceI;public OrganizationController(OrganizationServiceI organizationServiceI) {this.organizationServiceI = organizationServiceI;}@GetMapping(value = "/organization/getDepartmentsByCorpId/{corpId}/{scenario}")public MultiResponse<Department> listCustomerByName(@PathVariable("corpId") String corpId,@PathVariable("scenario") String scenario){OrgnizationQry qry = new OrgnizationQry();qry.setCorpId(corpId);qry.setIncludeDelete(true);qry.setBizScenario(BizScenario.valueOf("organize","getByCorpId",scenario));return organizationServiceI.getDepartmentsByCorpId(qry);}}

下面是使用接口进行测试的结果。

小结

基于元数据的扩展点设计,可以灵活的应对 业务场景的多样性,以及灵活的支持版本升级。 其它的扩展点(校验器,转换器)其它等,也可以轻松做到扩展。 使用例子在框架的单元测试用例中。

扩展点设计

设计本质

设计理念。是一种基于数据的配置扩展。即基于注解上带上配置数据。

@Extension 源码如下:

package com.alibaba.cola.extension;import com.alibaba.cola.common.ColaConstant;
import org.springframework.stereotype.Component;import java.lang.annotation.*;@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Component
public @interface Extension {String bizId()  default BizScenario.DEFAULT_BIZ_ID;String useCase() default BizScenario.DEFAULT_USE_CASE;String scenario() default BizScenario.DEFAULT_SCENARIO;
}

图文说明如下:

下面深入源码进行研究。从使用的源码出发。

ExtensionExecutor

类图如下。

首先,标注了Component,所以,在ioc中可以通过类型拿到实例。

最后,执行函数是放在父类AbstractComponentExecutor中;

重点分析一下它实现的功能:即通过坐标得到扩展实例;

 /*** if the bizScenarioUniqueIdentity is "ali.tmall.supermarket"** the search path is as below:* 1、first try to get extension by "ali.tmall.supermarket", if get, return it.* 2、loop try to get extension by "ali.tmall", if get, return it.* 3、loop try to get extension by "ali", if get, return it.* 4、if not found, try the default extension* @param targetClz*/protected <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) {checkNull(bizScenario);Ext extension;String bizScenarioUniqueIdentity = bizScenario.getUniqueIdentity();logger.debug("BizScenario in locateExtension is : " + bizScenarioUniqueIdentity);// first tryextension = firstTry(targetClz, bizScenarioUniqueIdentity);if (extension != null) {return extension;}// loop tryextension = loopTry(targetClz, bizScenarioUniqueIdentity);if (extension != null) {return extension;}throw new ColaException("Can not find extension with ExtensionPoint: "+targetClz+" BizScenario:"+bizScenarioUniqueIdentity);}

实现步骤如下:

ExtensionRepository

package com.alibaba.cola.extension;import java.util.HashMap;
import java.util.Map;import org.springframework.stereotype.Component;import lombok.Getter;/*** ExtensionRepository * @author fulan.zjf 2017-11-05*/
@Component
public class ExtensionRepository {@Getterprivate Map<ExtensionCoordinate, ExtensionPointI> extensionRepo = new HashMap<>();}

里面是一个空的map,主要还是看组装过程。看下面的ExtensionRegister;

ExtensionRegister

看名字,就是注册扩展的组件。

/** Copyright 2017 Alibaba.com All right reserved. This software is the* confidential and proprietary information of Alibaba.com ("Confidential* Information"). You shall not disclose such Confidential Information and shall* use it only in accordance with the terms of the license agreement you entered* into with Alibaba.com.*/
package com.alibaba.cola.boot;import com.alibaba.cola.common.ApplicationContextHelper;
import com.alibaba.cola.common.ColaConstant;
import com.alibaba.cola.exception.framework.ColaException;
import com.alibaba.cola.extension.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** ExtensionRegister * @author fulan.zjf 2017-11-05*/
@Component
public class ExtensionRegister implements RegisterI{@Autowiredprivate ExtensionRepository extensionRepository;@Overridepublic void doRegistration(Class<?> targetClz) {ExtensionPointI extension = (ExtensionPointI) ApplicationContextHelper.getBean(targetClz);Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class);String extPtClassName = calculateExtensionPoint(targetClz);BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario());ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(extPtClassName, bizScenario.getUniqueIdentity());ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extension);if (preVal != null) {throw new ColaException("Duplicate registration is not allowed for :" + extensionCoordinate);}}/*** @param targetClz* @return*/private String calculateExtensionPoint(Class<?> targetClz) {Class[] interfaces = targetClz.getInterfaces();if (ArrayUtils.isEmpty(interfaces))throw new ColaException("Please assign a extension point interface for "+targetClz);for (Class intf : interfaces) {String extensionPoint = intf.getSimpleName();if (StringUtils.contains(extensionPoint, ColaConstant.EXTENSION_EXTPT_NAMING))return intf.getName();}throw new ColaException("Your name of ExtensionPoint for "+targetClz+" is not valid, must be end of "+ ColaConstant.EXTENSION_EXTPT_NAMING);}}

注册过程如下:

以上是扩展类注册到扩展仓库的过程。

注册时机。启动的时刻通过包扫描进行注册。

RegisterFactory

把各种注册器放入到ioc中,通过一个统一的方法返回。

/** Copyright 2017 Alibaba.com All right reserved. This software is the* confidential and proprietary information of Alibaba.com ("Confidential* Information"). You shall not disclose such Confidential Information and shall* use it only in accordance with the terms of the license agreement you entered* into with Alibaba.com.*/
package com.alibaba.cola.boot;import com.alibaba.cola.command.Command;
import com.alibaba.cola.command.PostInterceptor;
import com.alibaba.cola.command.PreInterceptor;
import com.alibaba.cola.event.EventHandler;
import com.alibaba.cola.extension.Extension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** RegisterFactory** @author fulan.zjf 2017-11-04*/
@Component
public class RegisterFactory{@Autowiredprivate PreInterceptorRegister preInterceptorRegister;@Autowiredprivate PostInterceptorRegister postInterceptorRegister;@Autowiredprivate CommandRegister commandRegister;@Autowiredprivate ExtensionRegister extensionRegister;@Autowiredprivate EventRegister eventRegister;public RegisterI getRegister(Class<?> targetClz) {PreInterceptor preInterceptorAnn = targetClz.getDeclaredAnnotation(PreInterceptor.class);if (preInterceptorAnn != null) {return preInterceptorRegister;}PostInterceptor postInterceptorAnn = targetClz.getDeclaredAnnotation(PostInterceptor.class);if (postInterceptorAnn != null) {return postInterceptorRegister;}Command commandAnn = targetClz.getDeclaredAnnotation(Command.class);if (commandAnn != null) {return commandRegister;}Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class);if (extensionAnn != null) {return extensionRegister;}EventHandler eventHandlerAnn = targetClz.getDeclaredAnnotation(EventHandler.class);if (eventHandlerAnn != null) {return eventRegister;}return null;}
}

BootStrap

扫描java的class,进行ioc组装;

/** Copyright 2017 Alibaba.com All right reserved. This software is the* confidential and proprietary information of Alibaba.com ("Confidential* Information"). You shall not disclose such Confidential Information and shall* use it only in accordance with the terms of the license agreement you entered* into with Alibaba.com.*/
package com.alibaba.cola.boot;import java.util.List;
import java.util.Set;
import java.util.TreeSet;import org.springframework.beans.factory.annotation.Autowired;import com.alibaba.cola.exception.framework.ColaException;import lombok.Getter;
import lombok.Setter;/*** <B>应用的核心引导启动类</B>* <p>* 负责扫描在applicationContext.xml中配置的packages. 获取到CommandExecutors, intercepters, extensions, validators等* 交给各个注册器进行注册。** @author fulan.zjf 2017-11-04*/
public class Bootstrap {@Getter@Setterprivate List<String> packages;private ClassPathScanHandler handler;@Autowiredprivate RegisterFactory registerFactory;public void init() {Set<Class<?>> classSet = scanConfiguredPackages();registerBeans(classSet);}/*** @param classSet*/private void registerBeans(Set<Class<?>> classSet) {for (Class<?> targetClz : classSet) {RegisterI register = registerFactory.getRegister(targetClz);if (null != register) {register.doRegistration(targetClz);}}}

其它的核心组件的注册也在该代码中。

AbstractComponentExecutor

抽象的组件执行器,主要功能是定位到扩展类,然后执行接口的方法。

源码如下:

package com.alibaba.cola.boot;import com.alibaba.cola.extension.BizScenario;
import com.alibaba.cola.extension.ExtensionCoordinate;import java.util.function.Consumer;
import java.util.function.Function;/*** @author fulan.zjf* @date 2017/12/21*/
public abstract class AbstractComponentExecutor {/*** Execute extension with Response** @param targetClz* @param bizScenario* @param exeFunction* @param <R> Response Type* @param <T> Parameter Type* @return*/public <R, T> R execute(Class<T> targetClz, BizScenario bizScenario, Function<T, R> exeFunction) {T component = locateComponent(targetClz, bizScenario);return exeFunction.apply(component);}public <R, T> R execute(ExtensionCoordinate extensionCoordinate, Function<T, R> exeFunction){return execute((Class<T>) extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);}/*** Execute extension without Response** @param targetClz* @param context* @param exeFunction* @param <T> Parameter Type*/public <T> void executeVoid(Class<T> targetClz, BizScenario context, Consumer<T> exeFunction) {T component = locateComponent(targetClz, context);exeFunction.accept(component);}public <T> void executeVoid(ExtensionCoordinate extensionCoordinate, Consumer<T> exeFunction){executeVoid(extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);}protected abstract <C> C locateComponent(Class<C> targetClz, BizScenario context);
}

主要用到了java8的函数式接口Function<T,R>. T:即系统中注册好的扩展类实例; R即调用T的使用类的方法,执行之后的返回值。

把执行哪个方法的选择权交给了业务逻辑代码。

提供了4种不同的重载方法。

小结

通过key,value的方式进行扩展。

代码

代码点我获取!

原创不易,关注诚可贵,转发价更高!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。 我会持续分享Java软件编程知识和程序员发展职业之路,欢迎关注,我整理了这些年编程学习的各种资源,关注公众号‘李福春持续输出’,发送'学习资料'分享给你!

COLA的扩展性使用和源码研究相关推荐

  1. MapReduce多用户任务调度器——容量调度器(Capacity Scheduler)原理和源码研究

    前言:为了研究需要,将Capacity Scheduler和Fair Scheduler的原理和代码进行学习,用两篇文章作为记录.如有理解错误之处,欢迎批评指正. 容量调度器(Capacity Sch ...

  2. 区块链扩展性技术总结

    最近在做区块链的扩展性优化方面的研究学习,总结一下当前的主流区块链扩展性技术. 1 现状 区块链技术提高了数据的安全性与可靠性,但实际使用中存在可扩展性差的显著问题,出现了很多提升区块链扩展性的方案. ...

  3. 状态模式的介绍及状态机模型的函数库javascript-state-machine的用法和源码解析

    文章大体就两部分: 状态模式 状态机模型的函数库javascript-state-machine的用法和源码解析 场景及问题背景: 我们平时开发时本质上就是对应用程序的各种状态进行切换并作出相应处理. ...

  4. 面试官:怎么改进哈希算法实现负载均衡的扩展性和容错性?我:...

    面试官:怎么改进哈希算法实现负载均衡的扩展性和容错性? 什么是哈希算法 数据结构中我们学习过哈希表也称为散列表,我们来回顾下散列表的定义. 散列表,是根据键直接访问在指定储存位置数据的数据结构.通过计 ...

  5. Apache Camel源码研究之Language

    Apache Camel通过Language将Expression和Predicate的构造操作合并在一起,减少了概念,也降低了扩展难度,是的整体架构更加清晰. 1. 概述 Apache Camel为 ...

  6. WebRTC源码研究(1)WebRTC架构

    文章目录 WebRTC源码研究(1)WebRTC架构 1. WebRTC简介 2. WebRTC的能力 2.1 抓住属于WebRTC的5G时代风口 2.1.1 浏览器的支持情况 2.1.2 大厂的加入 ...

  7. 软件系统扩展性_我如何将软件系统的性能扩展35,000%

    软件系统扩展性 重点 (Top highlight) 一天处理超过$ 20,000,000 (Processing over $20,000,000 in a single day) A previo ...

  8. 【Spring Boot 源码研究 】- 自动化装配条件化配置Conditional剖析

    1. Spring Boot Condition功能与作用 @Conditional是基于条件的自动化配置注解, 由Spring 4框架推出的新特性. 在一个服务工程, 通常会存在多个配置环境, 比如 ...

  9. Google Test(GTest)使用方法和源码解析——自动调度机制分析

    在<Google Test(GTest)使用方法和源码解析--概况 >一文中,我们简单介绍了下GTest的使用和特性.从这篇博文开始,我们将深入代码,研究这些特性的实现.(转载请指明出于b ...

最新文章

  1. 拜托!不要用“ ! = null 做判空了
  2. GIT如何查看本地分支与远程分支的关联配置(git branch --set-upstream)
  3. python基础知识和运用
  4. boost::math模块两个 Lambert W 函数的最基本调用示例
  5. Table 'barfoo_datacenter_config.parttemplates' doesn't exist------Mysql
  6. python浮点数类型与数学_Python3标准库:decimal定点数和浮点数的数学运算
  7. 技巧:使用User Control做HTML生成
  8. FFmpeg AVFMT_NOFILE宏定义剖析
  9. Uncaught SyntaxError: Unexpected token ‘var‘
  10. Oracle游标 CURSOR实例详解
  11. php memcached存储对象,用于会话存储的Memcached或MySQL – PHP
  12. wifi分析仪怎么看哪个信道好_四大实用WiFi检测工具,调整信道避免干扰
  13. Python Gym ImportError cannot import name ‘rendering‘ from ‘gym.envs.classic_control‘
  14. Seaborn(三)调色板palette
  15. LDO的基础特性——热关断
  16. 产业巨头布局移动互联网 惠普比联想棋高一筹
  17. python为什么打不开py文件_python文件打不开如何解决
  18. IT人士必去的10个网站 .
  19. eclipse 更换国内镜像
  20. 谷歌AI助NASA发现第二个太阳系;传快手完成新一轮融资;摩拜起诉小广告公司丨价值早报

热门文章

  1. 瘦脸针价格 瘦脸针效果 瘦脸针副作用金霞微整形
  2. 就您所知,它只是一个Java库
  3. c语言count的作用,C语言中count是什么意思?
  4. 手机移动端首页广告栏滚动图片
  5. 如何利用爬虫工具实现竞品价格监控
  6. HttpClient设置HTTP请求头Header
  7. 描述一个完美的约会_在网上约会之前,要学习发现一个骗子
  8. 从画笔到像素:一文读懂AI绘画的前世与今生
  9. rtk 精确定位 简介
  10. 我对测试工作的一些认识(资深测试人员总结)