欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈
所谓 Dubbo 的内核是指, Dubbo 中所有功能都是基于它之上完成的,都是由它作为基础的。 Dubbo 内核的工作原理由四部分构成:服务发现机制 SPI、自适应机制 Adaptive、包装机制 Wrapper 与激活机制 Activate
。 Dubbo 通过这四种机制实现了对插件的 IoC、 AOP,实现了对自动生成类的动态编译 Compile。
1. SPI机制
SPI 全称为 Service Provider Interface
,是一种服务发现机制
。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类
。这样可以在运行时,动态为接口替换实现类
。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。
1.1 JDK 服务发现SPI机制
JDK 的 SPI 规范规定:
接口名
:可随意定义实现类名
:可随意定义提供者配置文件路径
: 其查找的目录为 META-INF/services提供者配置文件名称
:接口的全限定性类名,没有扩展名。提供者配置文件内容
:该接口的所有实现类的全限类性类名写入到该文件中,一个类名占一行
Java SPI 实际上是“基于接口的编程+策略模式+配置文件
”组合实现的动态加载机制。
1.1.1 入门案例
定义接口:
public interface SomeService {
void hello();
}
接下来定义两个实现类:
public class OneServiceImpl implements SomeService {
@Override
public void hello() {
System.out.println("执行OneServiceImpl的hello()方法");
}
}
public class TwoServiceImpl implements SomeService {
@Override
public void hello() {
System.out.println("执行TwoServiceImpl的hello()方法");
}
}
接下来 META-INF/services 文件夹下创建一个文件,名称为 SomeService 的全限定名com.abc.jdkspi.SomeService
。
文件内容为实现类的全限定的类名,如下:
com.abc.jdkspi.OneServiceImpl
com.abc.jdkspi.TwoServiceImpl
定义服务消费者类:
public class JdkSPITest {
public static void main(String[] args) {
// load()中放的是业务接口,其就相当于要加载的配置文件名
ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
Iterator<SomeService> it = loader.iterator();
while (it.hasNext()) {
SomeService service = it.next();
service.hello();
}
}
}
执行结果如下:
执行OneServiceImpl的hello()方法
执行TwoServiceImpl的hello()方法
1.1.2 总结
调用过程:
- 应用程序调用ServiceLoader.load方法,创建一个新的ServiceLoader,并实例化该类中的成员变量应用程序通过迭代器接口获取对象实例,ServiceLoader先判断成员变量providers对象中(LinkedHashMap<String,S>类型)是否有缓存实例对象,如果有缓存,直接返回. 如果没有缓存,执行类的装载。
优点: - 使用 Java SPI 机制的优势是实现解耦,使得接口的定义与具体业务实现分离,而不是耦合在一起。应用进程可以根据实际业务情况启用或替换具体组件。
缺点:
-
不能按需加载
。虽然 ServiceLoader 做了延迟载入,但是基本只能通过遍历全部获取,也就是接口的实现类得全部载入并实例化一遍。如果你并不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。 -
获取某个实现类的方式不够灵活
,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类
。 -
多个并发多线程使用 ServiceLoader 类的实例是不安全的。
-
加载不到实现类时抛出并不是真正原因的异常,错误很难定位
1.2 Dubbo的服务发现机制SPI
1.2.1 概述
Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制
。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader
类中,通过 ExtensionLoader,我们可以加载指定的实现类
1.2.2 规范定义
- 接口名:可以随意定义,但接口必须被
@SPI 注解修饰
- 实现类名:在接口名前添加一个用于表示自身功能的“标识前辍”字符串
- 提供者配置文件路径:在依次查找的目录为
- META-INF/dubbo/internal
- META-INF/dubbo
- META-INF/services
- 提供者配置文件名称:接口的全限定性类名,无需扩展名
- 提供者配置文件内容: 文件的内容为
key=value 形式
, value 为该接口的实现类的全限类性类名, key 可以随意,但一般为该实现类的“标识前辍”(首字母小写)。一个类名占一行。 - 提供者加载: ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader 类,用于加载提供者
配置文件中所有的实现类,并创建相应的实例。
1.2.3 入门案例
SPI接口类:
@SPI("wechat")
public interface Order {
// 支付方式
String way();
}
两个实现类:
public class AlipayOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用支付宝支付 ---");
return "支付宝支付";
}
}
public class WechatOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用微信支付 ---");
return "微信支付";
}
}
定义扩展类配置文件:
alipay=com.abc.spi.AlipayOrder
wechat=com.abc.spi.WechatOrder
测试类:
public class OrderTest {
public static void main(String[] args) {
// 获取到用于加载Order类型扩展类实例的extensionLoader实例
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order alipay = loader.getExtension("alipay");
System.out.println(alipay.way());
}
}
2. 自适应机制 Adaptive
Adaptive 机制,即扩展类的自适应机制
。即其可以指定想要加载的扩展名,也可以不指定。若不指定,则直接加载默认的扩展类。即其会自动匹配,做到自适应。其是通过@Adaptive
注解实现的。
2.1 @Adaptive 注解
@Adaptive 注解可以修饰类与方法,其作用相差很大。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
2.1.1 @Adaptive 修饰方法
被@Adapative 修饰的 SPI 接口中的方法称为 Adaptive 方法。在 SPI 扩展类中若没有找到 Adaptive 类,但系统却发现了 Adapative 方法,就会根据 Adaptive 方法自动为该 SPI 接口动 态生成一个 Adaptive 扩展类,并自动将其编译
。例如 Protocol 接口中就包含两个 Adaptive 方法。
2.1.2 @Adaptive 修饰类
有些 SPI 接口中的方法不需要 URL 相关的参数,此时就可以直接让@Adaptivate 来修饰 某个 SPI 接口的实现类,由该类实现对 SPI 扩展类的自适应。其是装饰者设计模式的应用。
2.2 Adaptive 方法规范
下面我们准备要定义 Adaptive 方法。那么 Adaptive 方法的定义有什么要求呢?我们通 过查看动态生成的 Adaptive 类来总结 Adaptive 方法的要求。
2.2.1 动态生成 Adaptive 类格式
package <SPI 接口所在包>;
public class SPI 接口名$Adpative implements SPI 接口 {
public adaptiveMethod (arg0, arg1, ...) {
// 注意,下面的判断仅对 URL 类型,或可以获取到 URL 类型值的参数进行判断
// 例如, dubbo 的 Invoker 类型中就包含有 URL 属性
if(arg1==null) throw new IllegalArgumentException(异常信息);
if(arg1.getUrl()==null) throw new IllegalArgumentException(异常信息);
URL url = arg1.getUrl();
// 其会根据@Adaptive 注解上声明的 Key 的顺序,从 URL 获取 Value,
// 作为实际扩展类。若有默认扩展类,则获取默认扩展类名;否则获取
// 指定扩展名名。
String extName = url.get 接口名() == null?默认扩展前辍名:url.get 接口名();
if(extName==null) throw new IllegalStateException(异常信息);
SPI 接口 extension = ExtensionLoader.getExtensionLoader(SPI 接口.class)
.getExtension(extName);
return extension. adaptiveMethod(arg0, arg1, ...);
}
public unAdaptiveMethod( arg0, arg1, ...) {
throw new UnsupportedOperationException(异常信息);
}
}
2.2.2 方法规范
从前面的动态生成的 Adaptive 类中的 adaptiveMethod()方法体可知, 其对于要加载的扩 展名的指定方式是通过 URL 类型的方法参数指定
的。所以对于 Adaptive 方法的定义规范仅 一条:其参数包含 URL 类型的参数,或参数可以获取到 URL 类型的值
。方法调用者是通过 URL 传递要加载的扩展名的。
2.3 Adaptive方法案例
接口类:
@SPI("wechat")
public interface Order {
String way();
@Adaptive
String pay(URL url);
}
实现类:
public class AlipayOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用支付宝支付 ---");
return "支付宝支付";
}
@Override
public String pay(URL url) {
System.out.println("--- pay 使用支付宝支付 ---");
return "pay 支付宝支付";
}
}
public class WechatOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用微信支付 ---");
return "微信支付";
}
@Override
public String pay(URL url) {
System.out.println("--- pay 使用微信支付 ---");
return "pay 微信支付";
}
}
配置文件:
alipay=com.abc.adaptive.method.AlipayOrder
wechat=com.abc.adaptive.method.WechatOrder
测试类:
public class AdaptiveMethodTest {
@Test
public void test01() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取Order的自适应类,不是任何一个Order的实现类
Order order = loader.getAdaptiveExtension();
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj");
System.out.println(order.pay(url));
// way()不是自适应方法,其调用会报错
// System.out.println(order.way());
}
@Test
public void test02() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取Order的自适应类,不是任何一个Order的实现类
Order order = loader.getAdaptiveExtension();
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj?order=alipay");
System.out.println(order.pay(url));
// way()不是自适应方法,其调用会报错
// System.out.println(order.way());
}
}
在执行时,会生成Order$Adaptive
类,如下:
public class Order$Adaptive implements com.abc.adaptive.method.Order {
public java.lang.String pay(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) {
throw new IllegalArgumentException("url == null");
}
org.apache.dubbo.common.URL url = arg0;
// order 参数是根据接口类名生成的,如GoodOrder 参数为good.order
String extName = url.getParameter("order", "wechat");
if (extName == null) {
throw new IllegalStateException(
"Failed to get extension (com.abc.adaptive.method.Order) name from url (" + url.toString()
+ ") use keys([order])");
}
com.abc.adaptive.method.Order extension =
(com.abc.adaptive.method.Order) ExtensionLoader.getExtensionLoader(com.abc.adaptive.method.Order.class)
.getExtension(extName);
return extension.pay(arg0);
}
public java.lang.String way() {
throw new UnsupportedOperationException(
"The method public abstract java.lang.String com.abc.adaptive.method.Order.way() of interface com.abc.adaptive.method.Order is not adaptive method!");
}
}
执行结果:
--- pay 使用微信支付 ---
pay 微信支付
--- pay 使用支付宝支付 ---
pay 支付宝支付
2.4 Adaptive 类 案例
接口:
@SPI("wechat")
public interface Order {
String way();
}
实现类:
public class AlipayOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用支付宝支付 ---");
return "支付宝支付";
}
}
public class WechatOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用微信支付 ---");
return "微信支付";
}
}
@Adaptive
public class AdaptiveOrder implements Order {
private String defaultName;
// 指定要加载扩展类的名称
public void setDefaultName(String defaultName) {
this.defaultName = defaultName;
}
@Override
public String way() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order;
if (StringUtils.isEmpty(defaultName)) {
// 加载SPI默认名称的扩展类
order = loader.getDefaultExtension();
} else {
// 加载指定名称的扩展类
order = loader.getExtension(defaultName);
}
return order.way();
}
}
配置:
alipay=com.abc.adaptive.aclass.AlipayOrder
wechat=com.abc.adaptive.aclass.WechatOrder
adaptive=com.abc.adaptive.aclass.AdaptiveOrder
测试类:
public class AdaptiveClassTest {
@Test
public void test01() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order = loader.getAdaptiveExtension();
System.out.println(order.way());
}
@Test
public void test02() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order = loader.getAdaptiveExtension();
((AdaptiveOrder) order).setDefaultName("alipay");
System.out.println(order.way());
}
// adaptive类不是直接扩展类
@Test
public void test04() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取到该SPI接口的所有直接扩展类
Set<String> supportedExtensions = loader.getSupportedExtensions();
System.out.println(supportedExtensions);
}
}
执行结果:
--- 使用微信支付 ---
微信支付
--- 使用支付宝支付 ---
支付宝支付
[alipay, wechat]
3. 包装机制 Wrapper
Wrapper 机制,即扩展类的包装机制
。就是对扩展类中的 SPI 接口方法进行增强,进行 包装,是 AOP 思想的体现
,是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper。
3.1 Wrapper 类规范
Wrapper 机制可以通过注解实现(dubbo3.0),也可以通过一套 Wrapper 规范实现的。Wrapper 类在定义时需要遵循如下规范:
- 该类要实现 SPI 接口
- 该类中要有 SPI 接口的引用
- 该类中 SPI 接口实例是通过仅包含一个 SPI 接口参数的带参构造器传的
- 在接口实现方法中要调用 SPI 接口引用对象的相应方法
- 该类名称以 Wrapper 结尾
3.2 案例
接口:
@SPI("wechat")
public interface Order {
// 支持方式
String way();
@Adaptive
String pay(URL url);
}
实现类:
public class AlipayOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用支付宝支付 ---");
return "支付宝支付";
}
@Override
public String pay(URL url) {
System.out.println("--- pay 使用支付宝支付 ---");
return "pay 支付宝支付";
}
}
public class WechatOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用微信支付 ---");
return "微信支付";
}
@Override
public String pay(URL url) {
System.out.println("--- pay 使用微信支付 ---");
return "pay 微信支付";
}
}
wrapper包装类:
public class OrderWrapper implements Order {
private Order order;
public OrderWrapper(Order order) {
this.order = order;
}
@Override
public String way() {
System.out.println("before-OrderWrapper对way()的增强");
String way = order.way();
System.out.println("after-OrderWrapper对way()的增强");
return way;
}
@Override
public String pay(URL url) {
System.out.println("before-OrderWrapper对pay()的增强");
String pay = order.pay(url);
System.out.println("after-OrderWrapper对pay()的增强");
return pay;
}
}
public class OrderWrapper2 implements Order {
private Order order;
public OrderWrapper2(Order order) {
this.order = order;
}
@Override
public String way() {
System.out.println("before-OrderWrapper222对way()的增强");
String way = order.way();
System.out.println("after-OrderWrapper222对way()的增强");
return way;
}
@Override
public String pay(URL url) {
System.out.println("before-OrderWrapper222对pay()的增强");
String pay = order.pay(url);
System.out.println("after-OrderWrapper222对pay()的增强");
return pay;
}
}
配置文件:
alipay=com.abc.wrapper.AlipayOrder
wechat=com.abc.wrapper.WechatOrder
wrapper=com.abc.wrapper.OrderWrapper
wrapper2=com.abc.wrapper.OrderWrapper2
测试类:
public class WrapperTest {
@Test
public void test01() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order = loader.getAdaptiveExtension();
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj");
System.out.println(order.pay(url));
}
@Test
public void test02() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order = loader.getAdaptiveExtension();
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj?order=alipay");
System.out.println(order.pay(url));
}
// wrapper类不是直接扩展类
@Test
public void test03() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取到该SPI接口的所有直接扩展类
Set<String> supportedExtensions = loader.getSupportedExtensions();
System.out.println(supportedExtensions);
}
}
执行结果:
before-OrderWrapper对pay()的增强
before-OrderWrapper222对pay()的增强
--- pay 使用微信支付 ---
after-OrderWrapper222对pay()的增强
after-OrderWrapper对pay()的增强
pay 微信支付
before-OrderWrapper对pay()的增强
before-OrderWrapper222对pay()的增强
--- pay 使用支付宝支付 ---
after-OrderWrapper222对pay()的增强
after-OrderWrapper对pay()的增强
pay 支付宝支付
[alipay, wechat]
4. 激活机制 Activate
用于激活扩展类的。Activate 机制,即扩展类的激活机制
。通过指定的条件来激活当前的扩展类
。其是通过 @Active 注解实现的。
4.1 @Activate 注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
String[] group() default {};
String[] value() default {};
String[] before() default {};
String[] after() default {};
int order() default 0;
}
在@Activate 注解中共有五个属性,其中 before、 after 两个属性已经过时,剩余有效属 性还有三个。它们的意义为:
group
: 为扩展类指定所属的组别, 是当前扩展类的一个标识。String[]
类型,表示一个扩展类可以属于多个组。value
: 为当前扩展类指定的 key,是当前扩展类的一个标识。String[]
类型,表示一个扩展类可以有多个指定的 key。order
: 指定筛选条件相同的扩展类的加载顺序。序号越小,优先级越高
。默认值为 0。
4.2 案例
接口:
@SPI("wechat")
public interface Order {
String way();
}
实现类:
// @Activate的order属性默认为0,order属性越小,其优先级越高
// @Activate的group属性与value属性均是用于给当前扩展类添加的标识
// group是一个“大范围标识”,value为一个“小范围标识”。
// 一个扩展类一旦指定了小范围标识value,那么这个大范围标识就不再起作用了
@Activate(group = "online", value = "alipay")
public class AlipayOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用支付宝支付 ---");
return "支付宝支付";
}
}
@Activate(group = {"online", "offline"}, order = 3)
public class CardOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用银联卡支付 ---");
return "银联卡支付";
}
}
@Activate(group = "offline", order = 4)
public class CashOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用现金支付 ---");
return "现金支付";
}
}
@Activate(group = "offline", order = 5)
public class CouponOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用购物券支付 ---");
return "购物券支付";
}
}
@Activate(group = "online")
public class WechatOrder implements Order {
@Override
public String way() {
System.out.println("--- 使用微信支付 ---");
return "微信支付";
}
}
配置文件:
alipay=com.abc.active.AlipayOrder
wechat=com.abc.active.WechatOrder
card=com.abc.active.CardOrder
cash=com.abc.active.CashOrder
coupon=com.abc.active.CouponOrder
测试类:
public class ActiveTest {
@Test
public void test01() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj");
// 一次性激活(加载)一批扩展类实例,这里指定的是激活所有group为“online"的扩展类
List<Order> online = loader.getActivateExtension(url, "", "online");
for (Order order : online) {
System.out.println(order.way());
}
}
@Test
public void test02() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj");
// 一次性激活(加载)一批扩展类实例,这里指定的是激活所有group为“offline"的扩展类
List<Order> online = loader.getActivateExtension(url, "", "offline");
for (Order order : online) {
System.out.println(order.way());
}
}
@Test
public void test03() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj?ooo=alipay");
// getActivateExtension()的参数二与三的关系是“或”
// 参数二指定的是要激活的value
// 参数三指定的是要激活的group
List<Order> online = loader.getActivateExtension(url, "ooo", "online");
for (Order order : online) {
System.out.println(order.way());
}
}
@Test
public void test04() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
URL url = URL.valueOf("xxx://localhost:8080/ooo/jjj?ooo=alipay");
List<Order> online = loader.getActivateExtension(url, "ooo", "offline");
for (Order order : online) {
System.out.println(order.way());
}
}
// activate类是直接扩展类
@Test
public void test05() {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取到该SPI接口的所有直接扩展类
Set<String> supportedExtensions = loader.getSupportedExtensions();
System.out.println(supportedExtensions);
}
}
执行结果:
--- 使用微信支付 ---
微信支付
--- 使用银联卡支付 ---
银联卡支付
--- 使用银联卡支付 ---
银联卡支付
--- 使用现金支付 ---
现金支付
--- 使用购物券支付 ---
购物券支付
--- 使用支付宝支付 ---
支付宝支付
--- 使用微信支付 ---
微信支付
--- 使用银联卡支付 ---
银联卡支付
--- 使用支付宝支付 ---
支付宝支付
--- 使用银联卡支付 ---
银联卡支付
--- 使用现金支付 ---
现金支付
--- 使用购物券支付 ---
购物券支付
[alipay, card, cash, coupon, wechat]
5. 总结
在配置文件中可能会存在四种类:普通扩展类、 Adaptive 类、 Wrapper 类,及 Activate 类
。它们的共同点是:都实现了 SPI 接口。
不同点是:
定义方式
: Adaptive 类与 Activate 类都是通过注解定义的。数量
: 一个 SPI 接口的Adaptive 类(无论是否是自动生成的)只会有一个
; Wrapper 类
与 Activate 类是可以有多个的。
直接扩展类
: 只有普通扩展类与 Activate 类是直接扩展类, Adaptive 类与 Wrapper 类不
是直接扩展类。
以上代码均在:Dubbo3.0源码注释githud地址 中dubbo-demo-xml-provider项目中。
6. 创建ExtensionLoader对象
ExtensionLoader类中的属性定义:
public class ExtensionLoader<T> {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
// ,分隔
private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
// 接下来是两个重要的静态变量,所有ExtensionLoader实例都共享这两个变量的值
// 静态变量存储的是SPI接口类和ExtensionLoader对象的映射关系.Dubble会为每一个SPI接口都会创建一个ExtensionLoader对象,并保存到EXTENSION_LOADERS变量中。
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
// 静态变量存储的是每个SPI接口的多个实现类和对应实例之间的关系。因此java进程中通常对应接口实现类对象是单例的。
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
// 当前SPI接口类
private final Class<?> type;
// 扩展类工厂
private final ExtensionFactory objectFactory;
// 扩展类和扩展名称映射关系
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
// 扩展名称和扩展类类型映射关系
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
// 扩展名称和自动激活扩展类映射关系
private final Map<String, Object> cachedActivates = Collections.synchronizedMap(new LinkedHashMap<>());
// 扩展名称和 自动激活扩展类的group属性集合
private final Map<String, Set<String>> cachedActivateGroups = Collections.synchronizedMap(new LinkedHashMap<>());
// 扩展名称和 自动激活扩展类的value属性集合
private final Map<String, String[]> cachedActivateValues = Collections.synchronizedMap(new LinkedHashMap<>());
// 扩展名称和扩展类实例映射关系,SPI类对应的实例,懒加载
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 自适应扩展类实例缓存,只能有一个
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
// 自适应扩展类
private volatile Class<?> cachedAdaptiveClass = null;
// SPI接口默认的扩展名称
private String cachedDefaultName;
private volatile Throwable createAdaptiveInstanceError;
// 包装扩展类集合
private Set<Class<?>> cachedWrapperClasses;
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
// 类路径加载策略,从那些路径加载扩展类
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
/**
* Record all unacceptable exceptions when using SPI
*/
private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
}
明白了ExtensionLoader 中各个属性的含义,我们看一下,是如何创建这个对象的:
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
对应的源码为:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// 若type为null,则抛出异常
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
// 若type不是接口,则抛出异常
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
// 若type接口没有被@SPI注解修饰,则抛出异常
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 从缓存map中获取接口SPI接口的extensionLoader实例
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
// 若为null,则创建一个并放入到缓存map
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
接下来看一下new ExtensionLoader<T>(type)
:
private ExtensionLoader(Class<?> type) {
// SPI接口
this.type = type;
// 用于创建当前type中指定“功能前辍”扩展名的实例
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
第一次创建,我们传进来的type
为 com.abc.active.Order
,并不是ExtensionFactory类,所以重新调用ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
对于ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
重新执行上面的逻辑,对于getAdaptiveExtension()
,这个是获取ExtensionFactory 对应的自适应类,该类的实现类如下:
只有AdaptiveExtensionFactory
该类有 @Adaptive 注解,这是一个自适应类,直接返回该类(详细执行步骤后面分析)。
实际上在创建ExtensionLoader实例时,有两次SPI的过程,一次是针对传进来的type类型,一次是ExtensionFactory类,如下图所示:
7. dubbo的SPI机制源码
7.1 从配置文件加载扩展点实现
以 1.2.3 小结中入门案例进行分析:
@Test
public void test01() {
// 获取到用于加载Order类型扩展类实例的extensionLoader实例
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order alipay = loader.getExtension("alipay");
System.out.println(alipay.way());
}
getExtension()如下:
public T getExtension(String name) {
// 注意第二个参数为true
T extension = getExtension(name, true);
if (extension == null) {
throw new IllegalArgumentException("Not find extension: " + name);
}
return extension;
}
public T getExtension(String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 若name指定为“true”,则加载SPI默认扩展名的实例
if ("true".equals(name)) {
return getDefaultExtension();
}
// 获取name对应的holder
final Holder<Object> holder = getOrCreateHolder(name);
// DCL
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建name对应的扩展类实例
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
return (T) instance;
}
private T createExtension(String name, boolean wrap) {
// 获取name的直接扩展类class
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
// 从缓存中获取该clazz对应的instance实例
T instance = (T) EXTENSION_INSTANCES.get(clazz);
// 实例为null,则创建一个实例,放入缓存
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 完成实例的IoC注入
injectExtension(instance);
if (wrap) {
// 将缓存中的wrapper set集合写入到list,并重新排序
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
// 遍历所有wrapper
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
// wrapperClass.getConstructor(type) 获取当前wrapper的单参构造器
// newInstance(instance) 调用单参构造器创建实例
// injectExtension() 调用wrapper的setter完成IoC注入
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
initExtension(instance);
// 返回最后wrapper的实例
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
从getExtensionClasses()
方法开始,来看看这个获取获取扩展点实现类class这个方法:
private Map<String, Class<?>> getExtensionClasses() {
// 从缓存中获取当前SPI接口的直接扩展类
Map<String, Class<?>> classes = cachedClasses.get();
// DCL
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加载并缓存“四类”,返回的是直接扩展类
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
// 返回的是直接扩展类
return classes;
}
首先先从cachedClasses 成员变量获取存储class 对象的map,这个cachedClasses 也是一个holder对象,只有一个volatile修饰的value,咱们cachedClasses这个holder的value值就是一个存储扩展点实现类class的map,然后接着往下,判断classes是否为空,如果为空的话加了个同步锁synchronized,然后再获取一下,再判断,咱们一般判空加锁的时候也应该再判断空,这样是防止重复加载的情况,接着往下走到loadExtensionClasses方法:
private Map<String, Class<?>> loadExtensionClasses() {
// 加载并缓存SPI接口的默认扩展类
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 从三种路径中将配置文件中的类加载并缓存
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
// 这个是加载com.alibaba包下的类,用于兼容dubbo2.6版本
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
// 类路径加载策略,从那些路径加载扩展类
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private void cacheDefaultExtensionName() {
// 从SPI接口中获取到SPI注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation == null) {
return;
}
// 获取SPI注解的value属性
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
// 使用逗号分隔value属性值
String[] names = NAME_SEPARATOR.split(value);
// 若分隔出了多个name,则直接抛出异常
if (names.length > 1) {
throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
// SPI接口中只能指定一个默认扩展名 将默认实现类的名字缓存起来
if (names.length == 1) {
cachedDefaultName = names[0];
}
}
}
首先cacheDefaultExtensionName()
是拿到扩展点接口上面的@SPI信息,然后拿到注解默认值,然后使用逗号分隔,如果多个话,就抛出异常,也就是只有一个是默认实现,然后缓存这个默认实现的name。
再往下就是创建了一个map,专门用来存储扩展实现类的class的,key就是你那个名字,然后value就是实现类class,再往下就是调用了三次loadDirectory方法,然后第二个参数传入的就是dubbo允许你扩展实现类配置文件的文件的位置,它们分别是:
- META-INF/dubbo/internal/
- META-INF/dubbo/
- META-INF/services/ 这个主要是兼容jdk的spi
接着我们再看下loadDirectory 方法:
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
// 拼接出要加载的文件名
String fileName = dir + type;
try {
// ----------------------- 将配置文件加载并转换为URL(start) ---------------
Enumeration<java.net.URL> urls = null;
//获取classloader
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
//使用类加载器获取Enumeration<java.net.URL> urls
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if (urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
// ----------------------- 将配置文件加载并转换为URL(end) ---------------
// 要加载的文件为什么会出现多个呢?在不同的模块下是可能存在相同文件的
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
// 加载配置文件内容
loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
首先是拼装装载文件的路径,由规定路径+ 接口类型全名。我们可以先看下,在dubbo中是怎样配置的:
我们可以看到配置文件 的文件名字就是扩展点接口的全路径名,然后我们在看下配置文件中具体的内容,就是“name=实现类全路径名
”的形式:
alipay=com.abc.dubbospi.AlipayOrder
上面这个是dubbo的,jdk的没有name 跟等号,上来就是实现类全类名,这个是jdk规定的。
再接着看loadDirectory这个方法,获取ClassLoader,如果获取到了就使用获取到的classloader获取资源,得到Enumeration<java.net.URL> urls,这个就跟迭代器一样的,没有获取到classloader就使用ClassLoader的静态方法获取资源,接着就是遍历这个urls,还有没有更多资源,如果有,就获取下一个资源url,然后执行loadResource方法。
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
try {
// try-with-resource语句 JDK7 使用reader 读取文件, 按照约定一行一行读取
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
String clazz = null;
// 逐行解析配置文件
while ((line = reader.readLine()) != null) {
// 解析注释行
final int ci = line.indexOf('#');
if (ci >= 0) {
line = line.substring(0, ci);
}
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
// 解析出功能性扩展名 名字
name = line.substring(0, i).trim();
// 解析出扩展类名 实现类
clazz = line.substring(i + 1).trim();
} else {
clazz = line;
}
if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
// 加载这个类
/**
* extensionClasses : 存储扩展的map, 整个查找扩展这块就是使用这个map
* resourceURL:资源url
* Class.forName(line, true, classLoader): 实现类class
* name:实现类的名字
*/
loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
}
}
...
}
}
}
} ...
}
首先这里是先new了个BufferedReader,因为咱们配置文件都是一行行的,所以这里使用BufferedReader来读取遍历,拿到一行后先将#开头的截取掉,就是将注释去掉,然后再用等号分割成一个name,一个line,这个name可以对应到上面咱们那个配置文件的dubbo,然后line对应的就是“com.abc.dubbospi.AlipayOrder
”,如果line小于0 抛出异常,大于0 ,进行loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name)这个方法,咱们代码中有对这几个参数的解释。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
// 判断当前clazz是否实现了当前的SPI接口类型type
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
// 判断当前clazz类上是否出现了@Adaptive注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 缓存这个clazz
cacheAdaptiveClass(clazz, overridden);
// 判断当前类是否是wrapper类
} else if (isWrapperClass(clazz)) {
// 缓存这个clazz
cacheWrapperClass(clazz);
// 对直接扩展类(普通扩展类与activate类)情况的处理
} else {
// 验证当前clazz是否具有无参构造器。
// 若没有,则直接抛出异常。若有,则当前clazz是扩展类
// 从这里可以知道,SPI直接扩展类要求,必须要有无参构造器
clazz.getConstructor();
// 若功能性扩展名为空,则为其找一个 兼容jdk的spi
if (StringUtils.isEmpty(name)) {
// 找一个扩展名
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 使用逗号将扩展名分隔为多个
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
// 仅使用第一个扩展名来缓存activate类
cacheActivateClass(clazz, names[0]);
// 遍历所有扩展名
for (String n : names) {
cacheName(clazz, n);
// 缓存扩展类的所有指定的扩展名
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
整体过程如下:
-
首先判断了一下实现类是否是接口的子类,不是的话抛出异常
-
缓存自适应扩展类这个cachedAdaptiveClass
: 接下来判断实现类上面有没有@Adaptive注解,如果有的话,再判断 cachedAdaptiveClass 是否是null,这个cachedAdaptiveClass 主要就是缓存带有@Adaptive注解的实现类的,然后没有的话,就将实现类class赋值给cachedAdaptiveClass 成员,然后有的话,就要判断是不是同一个实现类class,不是的话就抛出异常,这里主要是为了一个扩展点接口只能有一个@Adaptive 放到类上面的实现类。代码如下:private void cacheAdaptiveClass(Class<?> clazz, boolean overridden) { // 若缓存为null或缓存可覆盖,则将当前clazz写入缓存 if (cachedAdaptiveClass == null || overridden) { cachedAdaptiveClass = clazz; // 若当前缓存中的类与clazz不相同,则抛出异常 // 为什么?一个SPI接口只允许有一个Adaptive类,无论是自定义的,还是自动生成的 } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getName() + ", " + clazz.getName()); } }
-
缓存包装扩展类cachedWrapperClasses
: 接下来就是判断是不是包装类,他这里判断是否是包装类的依据就是判断有没有一个构造是传入自身类型。可以看下代码如果是包装类的话,就添加到cachedWrapperClasses 这个set中缓存起来。如下:private boolean isWrapperClass(Class<?> clazz) { try { // 获取当前clazz的单参构造器,且这个参数为SPI类型。 // 若该clazz没有这个构造器,则这里会抛出异常的。 // 若没有抛出,则说明当前clazz是个wrapper类 clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) { return false; } } private void cacheWrapperClass(Class<?> clazz) { if (cachedWrapperClasses == null) { cachedWrapperClasses = new ConcurrentHashSet<>(); } // 将clazz缓存到set集合。 // 注意,一个SPI允许有多个wrapper类 cachedWrapperClasses.add(clazz); }
-
对直接扩展类(普通扩展类与activate类)情况的处理
: 接下来就是判断name是否是空,如果为空的话就调用findAnnotationName方法获取一下name,咱们可以看下这个代码,private String findAnnotationName(Class<?> clazz) { // 获取当前类的@Extension注解 org.apache.dubbo.common.Extension extension = clazz.getAnnotation(org.apache.dubbo.common.Extension.class); // 若注解不空,则让注解的value值做扩展名 if (extension != null) { return extension.value(); } // 若没有注解,获取当前clazz的简单类名 String name = clazz.getSimpleName(); // 若类名是以SPI接口结尾,则截取类名中SPI接口名以外的部分,例如AlipayOrder截取出的是Alipay if (name.endsWith(type.getSimpleName())) { name = name.substring(0, name.length() - type.getSimpleName().length()); } // 若clazz类名不是以SPI结尾,则直接返回类名 // 要求是全小写字母 return name.toLowerCase(); }
主要就是判断有没有@Extension注解,如果没有的话,就要拿实现类的类名,然后判断实现类类名是否是以接口名为结尾的,如果是的话截取下要前面那块,如果不是以接口名为结尾的,就返回实现类的名字,如果有@Extension注解就直接拿注解里面的value值。这块name为空的操作,主要是为了兼容jdk spi。
接下来就是用逗号切割name,如果有name的话,再从实现类上get @Activate这个注解,这个注解主要是有激活的作用,如果Activate不为空的话,就放到cachedActivates 成员缓存起来, 接着就是遍历分割完的names,如果cachedNames 不包含 实现类class 的话就put进去缓存起来,如果extensionClasses中没有当前name的class就put进去缓存起来,如果有了的话,就要判断两个class是否相等,不相等话就要抛出异常了,因为同一个扩展点接口不允许有相同name的扩展点实现类的出现。
private void cacheName(Class<?> clazz, String name) { // 将第一个扩展名与clazz配对后缓存 if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, name); } } private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name, boolean overridden) { Class<?> c = extensionClasses.get(name); if (c == null || overridden) { extensionClasses.put(name, clazz); // 一个扩展名不能对应多个扩展类 } else if (c != clazz) { // duplicate implementation is unacceptable unacceptableExceptions.add(name); String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName(); logger.error(duplicateMsg); throw new IllegalStateException(duplicateMsg); } }
到最后咱们的 getExtensionClasses 方法返回的就是extensionClasses 这个里面放着以name为key ,扩展点实现类为value的map。然后缓存到cachedClasses
中。
7.2 setter注入过程(IOC注入)
我们来看下 injectExtension(instance) 这个方法,其中参数就是你这个扩展点实现类对象:
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
// 遍历当前实例类的所有方法
for (Method method : instance.getClass().getMethods()) {
// 若当前方法不是setter,则跳过
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
// 若当前setter方法上被@DisableInject修饰,则说明当前setter不会自动注入,则跳过
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获取当前setter的参数类型
Class<?> pt = method.getParameterTypes()[0];
// 若当前参数类型为基本数据类型,则跳过
// 即Dubbo的自动注入,只会注入对象
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取setter方法的参数名
String property = getSetterProperty(method);
// 创建当前setter的实参对象值。其依次尝试着使用SPI与Spring容器方式创建。
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 调用setter,完成注入
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
/**
* return true if and only if:
* <p>
* 1, public
* <p>
* 2, name starts with "set"
* <p>
* 3, only has one parameter
*/
private boolean isSetter(Method method) {
return method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers());
}
private String getSetterProperty(Method method) {
/**
* 判断 方法名的长度 >3
* 然后将第4个字符转成小写然后拼接后面的字符
* 比如说setName
* 获取到的property就是 name
*/
return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
}
首先 objectFactory != null 这句,主要为了判断你这个扩展点不是ExtensionFactory这个扩展点,接着遍历实现类的所有方法,然后方法得满足是set开头,只有一个参数,public方法
这三个条件才能有可能帮你注入,接着就是判断方法上面是否有@DisableInject注解,有这个注解就可以跳过去了,不会自动注入。
接着就是拿到参数的class,截取方法名中除了set那块,之后便是objectFactory.getExtension(pt, property);这个objectFactory一般是AdaptiveExtensionFactory的对象,
@Override
public <T> T getExtension(Class<T> type, String name) {
// 依次通过SPI与Spring两种方式获取实例
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
这里就能根据扩展点类型获取到具体的实现类,然后调用 method.invoke(instance, object)
设置进去。
7.3 包装Wrapper机制(AOP)
自动注入完就是自动包装了,这一块最好是看着代码说: createExtension(String name, boolean wrap)
中的包装机制代码如下:
if (wrap) {
// 将缓存中的wrapper set集合写入到list,并重新排序
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
// 遍历所有wrapper
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
// wrapperClass.getConstructor(type) 获取当前wrapper的单参构造器
// newInstance(instance) 调用单参构造器创建实例
// injectExtension() 调用wrapper的setter完成IoC注入
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
我们以4.2 案例为例,配置文件如下:
alipay=com.abc.wrapper.AlipayOrder
wechat=com.abc.wrapper.WechatOrder
wrapper2=com.abc.wrapper.OrderWrapper2
wrapper=com.abc.wrapper.OrderWrapper
两个wrapper类上加@Active注解,如下:
@Activate(order = 3)
public class OrderWrapper implements Order {
@Activate(order = 10)
public class OrderWrapper2 implements Order {
debug下: cachedWrapperClasses
如下:这个默认是以配置文件的顺序: 执行wrapperClassesList.sort(WrapperComparator.COMPARATOR);
代码后,如果实现类上有@Active注解并且属性order有值,按照order值升序排序,如下:
执行代码Collections.reverse(wrapperClassesList);
,将List中进行反转:
如果包装类上又@Active注解并且order不是默认值,就按照order值的大小(越小优先级越高),也就是order值越小在包装类的最外层。再执行包装类的方法时,从最外层开始执行,也就是order值越小越先执行。
结果输出如下:
before-OrderWrapper对pay()的增强
before-OrderWrapper222对pay()的增强
--- pay 使用微信支付 ---
after-OrderWrapper222对pay()的增强
after-OrderWrapper对pay()的增强
pay 微信支付
8. 自适应机制Adaptive源码
咱们在dubbo框架中经常会看到类似下面获取自适应实现类的代码,以2.4 案例进行源码分析:
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取Order的自适应类,不是任何一个Order的实现类
Order order = loader.getAdaptiveExtension();
接着我们就跟着getAdaptiveExtension()方法看下dubbo怎样实现自适应的。getAdaptiveExtension()
方法:
public T getAdaptiveExtension() {
// 从缓存中获取实例
Object instance = cachedAdaptiveInstance.get();
// 双重检测锁DCL
if (instance == null) {
// 判断之前有没有报错
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建自适应类实例
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
//抛出异常就记录下来
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
首先先从cachedAdaptiveInstance
成员中获取一下缓存着的自适应实现类对象,如果没有的话再判断createAdaptiveInstanceError是否是null,如果createAdaptiveInstanceError不是null,说明,之前再获取自适应实现类对象的时候报过错,然后被缓存下来了,接着就是执行createAdaptiveExtension方法来创建自适应实现类对象,如果抛出异常就会被记录下来,接着再看下createAdaptiveExtension方法:
private T createAdaptiveExtension() {
try {
// getAdaptiveExtensionClass() 获取adaptive类,是个.class
// newInstance() 是调用这个.class的无参构造器创建一个adaptive实例
// injectExtension() 完成adaptive实例的IoC注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
首先这里getAdaptiveExtensionClass()来获取自适应扩展实现对象的class,然后创建对象,再然后就是将对象传入injectExtension方法实现自动注入。
接着看下getAdaptiveExtensionClass方法看看是怎样获取class对象的。
private Class<?> getAdaptiveExtensionClass() {
// 将当前SPI接口的所有扩展类(四类:普通扩展类、adaptive类、wrapper类及activate类)
// 全部加载并进行缓存
getExtensionClasses();
// 若缓存中没有adaptive类,则创建一个
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 创建adaptive类(要求必须要有adaptive方法)
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
首先是获取所有的扩展实现类class,之前咱们分析过,会将实现类上带有@Adaptive注解的缓存到cachedAdaptiveClass 这个成员中。 那如果没有实现类上带有@Adaptive注解的时候,就要通过createAdaptiveExtensionClass来创建自适应扩展实现类了。
8.1 @Adaptive 注解在方法上
接着我们看下createAdaptiveExtensionClass是怎样创建自适应扩展实现类的:
private Class<?> createAdaptiveExtensionClass() {
ClassLoader classLoader = findClassLoader();
if (ApplicationModel.getEnvironment().getConfiguration().getBoolean(NATIVE, false)) {
try {
return classLoader.loadClass(type.getName() + "$Adaptive");
} catch (ClassNotFoundException e) {
//ignore
e.printStackTrace();
}
}
// 生成自适应类
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
// 获取到自适应compiler类实例
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 调用自适应compiler完成自动编译
return compiler.compile(code, classLoader);
}
首先是通过new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate()
来拼装成自适应实现类的代码,接着就是使用spi获取编译器,然后进行编译,加载,返回自适应实现类的class对象。 接下来看下generate()方法是怎样拼装代码的。
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
// 若没有@Adaptive方法,则直接抛出异常
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
// 拼接代码字符串
StringBuilder code = new StringBuilder();
code.append(generatePackageInfo());
code.append(generateImports());
code.append(generateClassDeclaration());
Method[] methods = type.getMethods();
for (Method method : methods) {
code.append(generateMethod(method));
}
code.append('}');
if (logger.isDebugEnabled()) {
logger.debug(code.toString());
}
return code.toString();
}
首先,获取接口的所有方法,判断方法上面是否有@Adaptive注解,只要有一个方法有注解就继续执行,没有的话就要抛出异常了。
接着就是拼装package,import,然后自适应扩展类名就是 接口名+$Adaptive
然后实现一下接口,接着就是遍历method, 判断method的上面是否有@Adaptive注解,如果没有将抛出异常的代码追加到code上,如果有@Adaptive注解,找出URL参数在参数列表的位置,如果有URL参数就拼装成判断是否是null,如果是null就抛出异常,然后设置到本地变量,类似下面这样
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
如果没有URL,就遍历参数类型里面有get方法并且get方法不是静态没有参数返回值是URL类型的,没有找到这么个条件的参数的时候,就会抛出异常 ,如果找到判断这个参数不是null,参数获取的URL不是null,然后URL设置到本地变量url中。
接着获取@Adaptive 注解值,如果没有值,就那class 名字来 如果参数有Invocation类型的,然后生成判断null的代码与String methodName = arg0.getMethodName(),并设置标志hasInvocation为true。接着就是遍历@Adaptive 上面value数组,这块主要是实现通过url来获取配置参数的值,这个值就是对应扩展实现类的name,判断extName如果是null的话就要抛出异常,接着就是通过getExtensionLoader(当前扩展接口).getExtension(extName) 来获取扩展实现类对象,然后再通过这个实现类对象调用自己对应的这个方法。
大家有兴趣的可自行研读一下,以2.4案例生成如下:
public class Order$Adaptive implements com.abc.adaptive.method.Order {
public java.lang.String pay(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) {
throw new IllegalArgumentException("url == null");
}
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("order", "wechat");
if (extName == null) {
throw new IllegalStateException(
"Failed to get extension (com.abc.adaptive.method.Order) name from url (" + url.toString()
+ ") use keys([order])");
}
com.abc.adaptive.method.Order extension =
(com.abc.adaptive.method.Order) ExtensionLoader.getExtensionLoader(com.abc.adaptive.method.Order.class)
.getExtension(extName);
return extension.pay(arg0);
}
public java.lang.String way() {
throw new UnsupportedOperationException(
"The method public abstract java.lang.String com.abc.adaptive.method.Order.way() of interface com.abc.adaptive.method.Order is not adaptive method!");
}
}
说白了,这个自适应就是根据接口自己实现一个实现类
,然后这个实现类是能够根据@Adaptive 配置的参数值然后去URL中获取对应的值,然后再根据这个值使用spi获取扩展实现类,最后调用这个实现类的对应方法。
8.2 @Adaptive 注解在类上
当@Adaptive在具体实现类上时,在加载classs的时候,就会被cachedAdaptiveInstance这个成员缓存起来,我们可以在回顾下加载class的时候是怎么处理@Adaptive 注解在类上的情况的。ExtensionLoader.loadClass
方法中:
// 判断当前clazz类上是否出现了@Adaptive注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 缓存这个clazz 缓存自适应拓展对象的类到 `cachedAdaptiveClass`
cacheAdaptiveClass(clazz, overridden);
}
首先判断你这个实现类上有没有@Adaptive注解,如果有的话再判断cachedAdaptiveClass是不是null,如果是null,直接将咱们找出来的这个class 缓存到cachedAdaptiveClass成员中,如果不是null,说明被加载过了,然后判断咱们加载出来的这个class是不是缓存的那个,如果不是的话,就说明同一个扩展点接口有两个带有@Adaptive的扩展实现类,这个dubbo是不允许的,所以直接抛出异常。也就是说,当@Adaptive在扩展实现类时,只允许有一个
。
9. 自动激活机制Active源码
9.1 具体实现
@Activate注解,这个注解就是标识自动激活的,主要是用在有多个扩展点实现,然后根据不同条件被激活的场景中,比如说Filter需要多个同时激活,因为每个Filter实现的是不同的功能。
讲解自动激活实现,需要举个例子,那就是我们dubbo框架中的Filter,ProtocolFilterWrapper 是Protocol扩展点实现类的包装类,在我们服务一启动 暴露服务的时候,我们会获取Protocol扩展点的实现类,我们得到的实现类是经过ExtensionLoader为我们包装过的实现类,其中就被ProtocolFilterWrapper包装过,当我们暴露服务的时候就会先执行ProtocolFilterWrapper中export方法,我们来看下代码
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {/// registry protocol 就到下一个wapper 包装对象中就可以了
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
首先判断这个Protocol是不是registry的,如果是的话就直接跳过的了,也就是没它什么事了。 如果不是就要走下面这个,这里调用了 buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)方法,其中 Constants.SERVICE_FILTER_KEY是service.filter,Constants.PROVIDER 是provider,我们来看下这个方法。
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker; // size = 8
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
//exception->moniter->timeout->trace->context->generic->classloader->echo
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
这个方法执行了List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
,这里我们本文的主角就登场了,获取了Filter的ExtensionLoader对象,然后调用getActivateExtension方法,参数分别是url,service.filter,provider,接着我们要看下这个方法了.
我们以案例4.2 进行分析:
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
接下来getActivateExtension
方法:
public List<T> getActivateExtension(URL url, String[] values, String group) {
// solve the bug of using @SPI's wrapper method to report a null pointer exception.
// 排序
Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
if (cachedActivateGroups.size() == 0) {
synchronized (cachedActivateGroups) {
// cache all extensions
if (cachedActivateGroups.size() == 0) {
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
cachedActivateValues.put(name, activateValue);
}
}
}
}
// traverse all cached extensions
cachedActivateGroups.forEach((name, activateGroup)->{
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(cachedActivateValues.get(name), url)) {
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
}
});
}
if (names.contains(DEFAULT_KEY)) {
// will affect order
// `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them
ArrayList<T> extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size());
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (!DEFAULT_KEY.equals(name)) {
if (containsExtension(name)) {
extensionsResult.add(getExtension(name));
}
} else {
extensionsResult.addAll(activateExtensionsMap.values());
}
}
}
return extensionsResult;
} else {
// add extensions, will be sorted by its order
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (!DEFAULT_KEY.equals(name)) {
if (containsExtension(name)) {
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
}
}
}
}
return new ArrayList<>(activateExtensionsMap.values());
}
}
首先将values放到names这个list,然后判断如果names这个list中没有-default
的话就继续执行,这个-default
是用户自己配置的,表示不使用这些filter。
接着就是调用getExtensionClasses(), 这个方法之前咱们说过,就是加载扩展点的所有实现类,这个方法执行完后,该扩展点的实现类会被分类缓存。
接着就是遍历cachedActivates 这个map了,这个map存的是扩展点实现类上的@Activate注解,key是name,值扩展点实现类上的@Activate注解。
接着就是获取注解上group属性,判断如果是这个group的,就继续根据name获取扩展点的实现类,接着再判断names里面没有包含这个name,然后也没有-name,之后就是activate的value 在url有对应参数,同时满足这个三个条件,然后会将这个扩展点实现类添加到开始定义好的activateExtensionsMap
中activateExtensionsMap
排序规则在ActivateComparator这个类中实现了Comparator 接口compare方法。
接着就是再遍历一下names,如果name没有-开头或者是names中没有-xxx ,然后就通过name获取到对应的扩展点实现,添加到这个activateExtensionsMap
中。
9.2 总结
@Activate有多个属性,我们再来回顾一下:
-
String[] group
,这个属性是分组的,在我们服务提供者就是provider,然后服务消费者那边就是consumer,你没配置组也就算了,如果你配置了组,就会把不是你这个组的给过滤掉。 -
String[] value
,这个参数也是个数组形式,他会查找url中如果包含该key值就会,就会激活。咱们上面代码中也看到了判断中有个isActive(activate, url) ,其实这个方法就是把activate的value值拿出来,跟url的所有key做比较,相等或者是.key ,然后url对应的value还不能是null,这才返回true。 -
String[] before,after
这两个属性在排序的时候用到了,before就是表示哪些扩展点在本扩展点前面,after就是表示哪些扩展点在本扩展点后面。已经被废弃了。 -
int order
这个也是在排序的时候使用,先按照 before跟after排,这个排好了直接返回了,最后才用这个order排序。这里还有个点就是用- 这个符号标识就可以不被激活,在filter属性上使用- 标识需要去掉的过滤器 比如:<dubbo:provider filter="-monitor" /> ,你也用-default来去掉所有
。
使用小总结:
@Activate
的order属性默认为0,order属性越小,其优先级越高@Activate
的group属性与value属性均是用于给当前扩展类添加的标识- group是一个“大范围标识”,value为一个“小范围标识”。
- 一个扩展类一旦指定了小范围标识value,那么这个大范围标识就不再起作用了
-
// getActivateExtension()的参数二与三的关系是“或” // 参数二指定的是要激活的value // 参数三指定的是要激活的group public List<T> getActivateExtension(URL url, String key, String group)