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!"); } }