系列文章:
一、前言
通过前三篇文章的介绍,我们了解到了系统腐化的原因和过程,以及一种提高系统扩展性的设计思想。本文将参考淘宝的TMF框架的思想,落地一套扩展点框架,为了体现框架提供的核心功能,因此起名为easy-extension。
项目地址: github.com/xiaoshicae/…
二、easy-extension框架期望实现的目标效果
以spring-web项目为例,扩展点会根据本次请求命中的业务,动态执行扩展点实现。实现的效果如下 :
@RestController
@RequestMapping("/api")
public class Controller {
// 动态注入扩展点,不同业务会有不同实现,业务挂在的能力也会有不同的实现
// 最终会根据业务及能力叠加,注入优先级最高的实现
@Autowired
private Extension ext; // 此处注入的是扩展点的动态代理
@RequestMapping("/process")
public String process() {
// 动态执行扩展点的实现,根据命中的业务,生效的能力,执行优先级最高的实现
// 如果命中的业务和挂载的能力都没实现该扩展点,系统会提供默认实现进行兜底
String res = ext.doSomething();
return res;
}
}
// 扩展点定义
interface Extension {
String doSomething();
}
回答一下上一篇留下的问题,如果命中的业务和挂载的能力均没有实现该扩展点怎么处理 ?答案是:系统会为每个扩展点提供一个默认兜底实现)。
三、easy-extension框架运行大致流程图
- 框架启动流程:扫描所有的扩展点定义,业务,能力,默认扩展点实现到spring容器,自动装配扩展点的动态代理。
- 请求处理流程:初始化session,匹配业务和能力,按优先级执行扩展点的实现,请求结束前需要清理下session。
四、easy-extension框架使用详细步骤
完整的框架使用流程的demo地址,可以参考: github.com/xiaoshicae/…
- maven引入
<dependency>
<groupId>io.github.xiaoshicae</groupId>
<artifactId>easy-extension-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
- 扩展点定义
// 扩展点,需要@ExtensionPoint注解,以便spring包扫描识别
@ExtensionPoint
public interface Extension1 {
String doSomething1();
}
@ExtensionPoint
public interface Extension2 {
String doSomething2();
}
- 定义用于身份和能力匹配的参数结构
// 用于业务或者能力匹配的参数
public class MyParam {
private final String name;
public MyParam(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
- 定义默认能力(匹配不到任何实现时,默认兜底实现)
// 默认能力,必须实现所有@ExtensionPoint注解标注的扩展点
// 业务没有实现某个扩展点时,默认能力作为兜底实现
// 需要@Component注解,以便spring包扫描识别
@Component
public class DefaultAbility extends BaseDefaultAbility<MyParam> implements Extension1, Extension2 {
@Override
public String doSomething1() { // 默认兜底的能力必须实现所有的扩展点
return "default doSomething1";
}
@Override
public String doSomething2() { // 默认兜底的能力必须实现所有的扩展点
return "default doSomething2";
}
}
- 定义能力
// 能力X,实现了扩展点
// 需要@Component注解,以便spring包扫描识别
@Component
public class AbilityX extends AbstractAbility<MyParam> implements Extension1 {
// 能力X的code
@Override
public String code() {
return "app.ability.x";
}
// 能力X生效条件
@Override
public Boolean match(MyParam param) {
return param != null && param.getName().equals("x");
}
// 能力X实现了扩展点
@Override
public String doSomething1() {
return "abilityX doSomething1";
}
}
- 定义业务 & 挂载能力
// 业务A,实现了扩展点1和扩展点2
// 需要@Component注解,以便spring包扫描识别
@Component
public class BusinessA extends AbstractBusiness<MyParam> implements Extension1, Extension2 {
// 业务A身份唯一标识
@Override
public String code() {
return "app.business.a";
}
// 命中业务A的生效条件
@Override
public Boolean match(MyParam param) {
return param != null && param.getName().equals("a");
}
// 优先级(复杂场景下,业务挂载了能力,可能存在扩展点冲突,需要通过优先级解决冲突)
@Override
public Integer priority() {
return 100;
}
// 业务A挂载了能力X,且优先级50,高于自身的优先级100 (UsedAbility参考后面的定义)
@Override
public List<UsedAbility> usedAbilities() {
return List.of(new UsedAbility("app.ability.x", 50));
}
// 业务A实现了扩展点1
@Override
public String doSomething1() {
return "businessA doSomething1";
}
// 业务A实现了扩展点2
@Override
public String doSomething2() {
return "businessA doSomething2";
}
}
class UsedAbility {
private String code;
private Integer priority;
public UsedAbility(String code, Integer priority) {
this.code = code;
this.priority = priority;
}
}
- 定义interceptor,在每次请求时,注入用于身份和能力匹配的参数
@Component
public class Interceptor implements HandlerInterceptor {
// 框架启动会自动注入ISessionManager,可以直接通过spring自动注入获取
@Resource
private ISessionManager<MyParam> sessionManager;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 根据入参构造用于business和ability匹配的参数
String name = request.getParameter("name") != null ? request.getParameter("name").trim() : "unknown";
// 初始化session
sessionManager.initSession(new MyParam(name));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清空session
sessionManager.removeSession();
}
}
// 注入Interceptor
@Configuration
public class WebInterceptorConfigurer implements WebMvcConfigurer {
@Resource
private Interceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("/**").excludePathPatterns("/favicon.ico");
}
}
四、小结
本文介绍了基于淘宝的TMF框架的思想,落地一套扩展点框架。这套思想最大的优势是,实现了通用代码和扩展代码的分离,中台只沉淀通用的交易流程,扩展功能下放到了业务进行实现,实现了系统关注点的分离。下篇文章将会讨论下,这套框架适用场景,可能碰到的潜在问题,以及一些迭代优化方向。