Adaptive机制,即扩展类的自适应机制;其可以指定想要加载的扩展类名,也可以不指定,若不指定则直接加载默认的扩展类,会自动匹配,做到自适应,通过@Adaptive注解实现;
1 @Adaptive注解
@Adaptive注解可以修饰类与方法,其作用相差很大;
1.1 @Adaptive修饰类
被@Adaptive修饰的SPI接口扩展类称为Adaptive类,表示该SPI扩展类会按照该类中指定的方式获取,即用于固定实现方式,其是装饰者设计模式的应用;dubbo中只有两个@Adaptive修饰类;
1.1.1 dubbo源码
-
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
/**
* AdaptiveExtensionFactory
* 指定ExtensionFactory获取方式,就一种SPI
* 由@Adaptive修饰的类,只能固定的使用getExtension(Class<T> type, String name)
* 方法中写死的方式获取Extension;
*/
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
//@Adaptive修饰类固定的方式获取Extension的方法
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
- org.apache.dubbo.common.compiler.support.AdaptiveCompiler
提供两种编译方式:jdk与javassist,默认是javassist;
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
/**
* AdaptiveCompiler. (SPI, Singleton, ThreadSafe)
* 限定只能通过两种方式编译,只提供了jdk与javassist
*/
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
@Override
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
}
1.1.2 示例
(1)创建工程adaptiveclass导入dubbo依赖
<!-- dubbo依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.0</version>
</dependency>
(2)定义提供者路径及配置文件
alipay=com.zxy.spi.extension.AlipayOrder
wechat=com.zxy.spi.extension.WeChatOrder
adaptive=com.zxy.spi.extension.AdaptiveOrder
(3)定义接口及实现类
/**
* SPI标签,设置默认为alipay阿里支付
*/
@SPI("alipay")
public interface Order {
String way();
}
/**
* 自适应(扩展)实现类
*/
@Adaptive
public class AdaptiveOrder implements Order{
private String orderName;
public void setOrderName(String orderName) {
//通过名字(key)指定要加载哪一种支付方式
this.orderName = orderName;
}
@Override
public String way() {
//获取ExtensionLoader
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
Order order;
if(StringUtils.isEmpty(orderName)) {
//如果未指定则获取默认支付方式
order = loader.getDefaultExtension();
}else {
//安装指定名称获取支付方式
order = loader.getExtension(orderName);
}
return order.way();
}
}
public class AlipayOrder implements Order{
public String way() {
// TODO Auto-generated method stub
System.out.println("---支付宝way()---");
return "支付宝支付方式";
}
}
public class WeChatOrder implements Order{
public String way() {
System.out.println("---微信way()---");
return "微信支付方式";
}
}
(4)定义测试启动类
public class OrderTest {
public static void main(String[] args) {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取自适应AdaptiveExtension
Order adaptiveExtension = loader.getAdaptiveExtension();
//未指定
System.out.println(adaptiveExtension.way());
//指定支付方式
((AdaptiveOrder)adaptiveExtension).setOrderName("wechat");
System.out.println(adaptiveExtension.way());
}
}
1.2 @Adaptive修饰方法
被@Adaptive修饰的SPI接口中的方法称为Adaptive方法,在SPI扩展类中若没有找到Adaptive类,但系统却发现了Adaptive方法,就会根据Adaptive方法自动为该SPI接口动态生成一个Adaptive扩展类,并自动将其编译,修饰方法非常多,例如Protocol接口中就包含两个Adaptive方法;
1.2.1 dubbo源码
filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
(1)动态生成Adaptive类格式
package<SPI 接口所在包>
public class SPI接口名$Adpative implements SPI接口{
//arg参数要么是URL类型,要么是能获取到URL
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 exName = url.get接口名()==null?默认扩展前缀名:url.get接口名();
if(extName==null)throw new IllegalArgumentException(异常信息);
SPI接口 extension = ExtensionLoader.getExtensionLoader(SPI接口.class).getExtension(exName);
return extension.adaptiveMethod(arg0,arg1,...);
}
//非Adaptive方法
public unAdaptiveMethod(arg0,arg1,...) {
throw new UnsupportedOperationException(异常信息);
}
}
(2)方法规范
从前面的动态生成的Adaptive类中的adpativeMethod()方法体可知,其对于要加载的扩展名的指定方式是通过URL类型的方法参数指定的,所以对于Adaptive方法的定义规范仅一条:其参数包含URL类型参数,或参数可以获取到URL类型的值;方法调用者是通过URL传递要加载的扩展名;
1.2.2 示例
(1)创建工程adaptivemethod导入dubbo依赖
<!-- dubbo依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.0</version>
</dependency>
(2)创建提供者路径及配置文件
alipay=com.zxy.spi.extension.AlipayOrder
wechat=com.zxy.spi.extension.WeChatOrder
(3)创建接口及实现类
/**
* SPI标签,设置默认为alipay阿里支付
*/
@SPI("alipay")
public interface Order {
//支付方式
String way();
//支付
@Adaptive
String pay(URL url);
}
public class AlipayOrder implements Order{
public String way() {
System.out.println("---支付宝way()---");
return "支付宝支付方式";
}
@Override
public String pay(URL url) {
System.out.println("---支付宝pay()---");
return "使用支付宝支付";
}
}
public class WeChatOrder implements Order{
public String way() {
System.out.println("---微信way()---");
return "微信支付方式";
}
@Override
public String pay(URL url) {
System.out.println("---微信pay()---");
return "使用微信支付";
}
}
(4)定义测试启动类
public class OrderTest {
public static void main(String[] args) {
ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
// 获取自适应AdaptiveExtension
// 此处设置Debug断点,将自动生成的Order$Adaptive类手动取出
Order adaptiveExtension = loader.getAdaptiveExtension();
//模拟一个URL,不指定支付方式使用默认
URL url = URL.valueOf("xxx://localhost/ooo");
System.out.println(adaptiveExtension.pay(url));
/*
* 指定支付方式,参数必须为业务接口名(order 手动小写)
* 注意:此处有一个细节,如果接口名是两个单词构成,如OrderService,
* 那么此处参数为order.service
*/
URL url1 = URL.valueOf("xxx://localhost/ooo?order=wechat");
System.out.println(adaptiveExtension.pay(url1));
}
}
(5)将自动生成的Order$Adaptive类手动取出
从code的value中获取生成的Order$Adaptive代码;
在指定包创建Order$Adaptive,复制此代码:
package com.zxy.spi;
import org.apache.dubbo.common.extension.ExtensionLoader;
/**
* 将自动生成的Order$Adaptive类手动取出
*
*/
public class Order$Adaptive implements com.zxy.spi.Order {
//此方法是Adaptive方法可以调用,可以做自适应
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", "alipay");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.zxy.spi.Order) name from url(" + url.toString() + ") use keys([order])");
com.zxy.spi.Order extension = (com.zxy.spi.Order)ExtensionLoader.getExtensionLoader(com.zxy.spi.Order.class).getExtension(extName);
//程序最终走到此处
return extension.pay(arg0);
}
//此方法不是Adaptive方法不可以做自适应,调用抛出异常
public java.lang.String way() {
throw new UnsupportedOperationException("method public abstract java.lang.String com.zxy.spi.Order.way() of interface com.zxy.spi.Order is not adaptive method!");
}
}