最近,项目中使用了dubbo来实现服务化,所以近几天学习了dubbo相关的内容。dubbo是一个方便扩展的服务框架,所以在dubbo的架构设计中始终保持了良好的依赖扩展机制:微内核+插件。简而言之就是让扩展者可以和项目开发者拥有一样的灵活度,这也是dubbo得以迅速流行的一个必要条件。要想实现这种自由度,除了在架构分层组件上要保持高内聚低耦合外,底层也需要一套强大的类管理工具。在javaEE世界里,把这份工作做到极致的也已经有成熟的标准规范:OSGi。不过OSGi并不完全适配dubbo的需求,而且这玩意儿也有些过于重了,所以在此基础上,dubbo结合JDK标准的SPI机制设计出来一个轻量级的实现:Cooma。
关于Cooma
Cooma是一个极简、灵活的开源Java微容器(microcontainer)实现。以下是摘自Cooma官网的相关描述:
概念上极简独立
- 完全割除与容器不相关的概念。
- 审视已有的概念,谨慎引入新概念。
实现上简单直白
- 实现代码 少于1000行(不含代码注释)。使用sloccount统计0.2.1版本的实现代码是 780行。
- 保持代码可读性。
- 不考虑没有实际应用的概念。
- 谨慎对待性能优化;不能因此打乱代码的简单性。
集成上的简易灵活
- 保持灵活的扩展性。
- 开发者友好。
- 集成是否简易是设计是否良好的有效指标。
功能
- 以插件方式加载扩展
- 支持依赖扩展点的自动加载(扩展的IOC)
- 可以有扩展点Wrapper,为扩展写公共Filter代码(扩展的AOP)
- 统一的配置方式来配置各级扩展点
- 查询插件实现
- 支持配置方式/编程方式
核心概念
- Extension Point,扩展点,要扩展的接口
- Extension,扩展,即扩展点的实现
- Extension Instance,扩展实例,即扩展点实现类的实例,# ExtensionLoader中维护了Extension Instance的 单例。
- Extension Adaptive Instance,扩展的自适应实例
- Extension Wrapper,所有扩展点实现调用时都会先触发的执行
简单示例
示例的扩展点是Car
,有两个扩展点实现RacingCar
、SportCar
。
1) 接口类
package com.alibaba.demo.cooma;
import com.alibaba.cooma.Extension;
@Extension
public interface Car {
void run();
}
2) 实现类
package com.alibaba.demo.cooma;
public class RacingCar implements Car {
public void run() {
System.out.println("RacingCar Running...");
}
}
package com.alibaba.demo.cooma;
public class SportCar implements Car {
public void run() {
System.out.println("SportCar Running...");
}
}
3) 扩展点配置文件
在类路径的META-INF/extensions/
添加配置文件 com.alibaba.demo.cooma.Car
(文件名是接口的全限定名,即包括包名的接口名)。
给出扩展点实现的名称和实现类的信息。
内容:
racing=com.alibaba.demo.cooma.RacingCar
sport=com.alibaba.demo.cooma.SportCar
4) Main代码
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car racingCar = extensionLoader.getExtension("racing"); // 获取指定名的扩展实例
racingCar.run();
Car sportCar = extensionLoader.getExtension("sport"); // 获取指定名的扩展实例
sportCar.run();
上面代码的运行结果,如下:
RacingCar Running...
SportCar Running...
说明
ExtensionLoader维护扩展点实现实例的 单例,即getExtension
方法对于同一个扩展点实现名返回的实例是同一个。
# 后面会说到Wrapper
,当使用Wrapper
功能时,getExtension
方法返回的是Wrapper
实例。
实现的代码中和Cooma
相关的内容是:
- 扩展点接口上要加上
@Extension
注解。如果没有此注解ExtensionLoader
调用会出异常,拒绝管理。 - 在类路径的
META-INF/extensions/
添加配置文件com.alibaba.demo.cooma.Car
(文件名是接口的全限定名),给出扩展点实现的名称和实现类的信息。
这个使用方式可以通过扩展点实现名获得 单个接口的指定实现。实现的项目中往往会有多个扩展点配合。这样就需要扩展点中引用其它的扩展点,即注入相关的扩展点。在说明这个功能前,说明一下扩展点的“自适应实例(Adaptive Instance)”的功能。
关于Dubbo
首先先看一下dubbo的SPI部分的代码结构:
其中,Activate #自动激活加载扩展的注解;Adaptive #自适应扩展点的注解;ExtensionFactory #扩展点对象生成工厂接口;ExtensionLoader #扩展点加载器,扩展点的查找,校验,加载等核心逻辑的实现类;SPI #扩展点注解。
我们首先看一下最重要的ExtensionLoader文件。
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
以上是ExtensionLoader的构造方法,其中它初始化了type和objectFactory,前者为要作为扩展点的接口类型,后者表示要如何获取指定名称的扩展点实例(工厂类),目前dubbo提供了2个实现类(AdaptiveExtensionFactory,SpiExtensionFactory)。
@SuppressWarnings("unchecked") public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) throw new IllegalArgumentException("Extension type == null"); if(!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } if(!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); }ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }</pre>
以上这个方法是通过type获取到相应的loader。type与loader的对应关系是存储在一个ConcurrentMap中的。
具体的调用关系可以见下图(图片来自于网络)
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于