分布式RPC系统框架Dubbo-22Dubbo的内核解析-Adaptive机制

    科技2024-01-29  94

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

     

    Processed: 0.012, SQL: 8