dubbu 扩展注解_Dubbo之扩展机制(二)Adaptive【URL-动态适配】

interface com.alibaba.dubbo.cache.CacheFactory

interface com.alibaba.dubbo.common.compiler.Compiler

interface com.alibaba.dubbo.common.extension.ExtensionFactory

interface com.alibaba.dubbo.common.serialize.Serialization

interface com.alibaba.dubbo.common.store.DataStore

interface com.alibaba.dubbo.common.threadpool.ThreadPool

interface com.alibaba.dubbo.monitor.MonitorFactory

interface com.alibaba.dubbo.registry.RegistryFactory

interface com.alibaba.dubbo.remoting.Codec2

interface com.alibaba.dubbo.remoting.Dispatcher

interface com.alibaba.dubbo.remoting.Transporter

interface com.alibaba.dubbo.remoting.exchange.Exchanger

interface com.alibaba.dubbo.remoting.telnet.TelnetHandler

interface com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter

interface com.alibaba.dubbo.rpc.ExporterListener

interface com.alibaba.dubbo.rpc.Filter

interface com.alibaba.dubbo.rpc.InvokerListener

interface com.alibaba.dubbo.rpc.Protocol

interface com.alibaba.dubbo.rpc.ProxyFactory

interface com.alibaba.dubbo.rpc.cluster.Cluster

interface com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory

interface com.alibaba.dubbo.rpc.cluster.LoadBalance

interface com.alibaba.dubbo.rpc.cluster.RouterFactory

interface com.alibaba.dubbo.validation.Validation

官网定义

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。

我自已的理解:Adaptive是对SPI的二次包装。Dubbo一般不直接使用SPI这种方式,而是直接使用Adaptive来间接使用SPI这种方式。

一、使用示例

@SPI("ali") // 默认的值支付宝支付

public interface Pay {

// 接口的方法需要添加这个注解,在测试代码中,参数至少要有一个URL类型的参数

@Adaptive({"paytype"}) // 付款方式

void pay(URL url);

}

public class AliPay implements Pay {

@Override

public void pay(URL url) {

System.out.println("使用支付宝支付");

}

}

public class WechatPay implements Pay {

@Override

public void pay(URL url) {

System.out.println("使用微信支付");

}

}

在/dubbo-common/src/main/resources/META-INF/services/com.test.Pay文件下添加内容如下:

wechat = com.test.WechatPay

ali = com.test.AliPay

说明:Adaptive 可注解在类或方法上。当 Adaptive 注解在类上时,Dubbo 不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo 则会为该方法生成代理逻辑。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive 是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。Adaptive 注解的地方不同,相应的处理逻辑也是不同的。

测试

public static void main(String[] args) {

ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Pay.class);

Pay pay = loader.getAdaptiveExtension();

pay.pay(URL.valueOf("http://localhost:9999/xxx")); // 使用支付宝支付

pay.pay(URL.valueOf("http://localhost:9999/xxx?paytype=wechat")); // 使用微信支付

}

这里也会生成一个代理类 PayService$Adaptive,这种代理类实现AOP功能

package com.test;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class PayService$Adaptive implements com.test.PayService {

public void pay(com.alibaba.dubbo.common.URL arg0) {

if (arg0 == null)

throw new IllegalArgumentException("url == null");

com.alibaba.dubbo.common.URL url = arg0;

// 1.从 URL 中获取指定的SPI的扩展名称

String extName = url.getParameter("paytype", "ali"); // 从URL中获取key为paytype参数的value,如果获取不到,则使用默认@SPI注解上的值

if(extName == null)

throw new IllegalStateException("Fail to get extension(com.test.PayService) name from url(" + url.toString() + ") use keys([0])");

// 2.通过 SPI 加载具体的实现类

com.test.PayService extension = (com.test.PayService)ExtensionLoader.getExtensionLoader(com.test.PayService.class)

.getExtension(extName);

// 3.调用目标方法

extension.pay(arg0);

}

}

二、SPI与Adaptive比较

如果通SPI方式调用则是,每次都要传参,硬编码方式,不是很方便

PayService wechatPay = extensionLoader.getExtension("wechatPay");

wechatPay.pay(20);

如果是通过Adaptive方式,我们不用关心URL是否动态的,因为生成的代理类在其方法中会自动适配,符合模块接口设计的可插拔原则。

PayService payService = extensionLoader.getAdaptiveExtension();

payService.pay(url);

以ProxyFactory调用为例

public class ReferenceConfig extends AbstractReferenceConfig {

// 类在加载阶段这个值为 ProxyFactory$Adaptive类型

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

private T createProxy(Map map) {

// 省略若干代码...

return (T) proxyFactory.getProxy(invoker);

}

}

假设有这样一个 url 参数传入:

zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello

当执行proxyFactory.getProxy(invoker)方法时,

即执行

com.alibaba.dubbo.common.URL url = invoker.getUrl();

String extName = url.getParameter("proxy", "javassist");

ProxyFactory extension = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName);

通过String extName = url.getParameter("proxy", "javassist"); 从URL参数中获取proxy的值,没有就使用默认的值,即值为javassist;

通过ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName) 获取到了StubProxyFactoryWrapper这个类

然后通过StubProxyFactoryWrapper调用目标方法

三、源码分析

直接看生成code部分吧,其它方法的逻辑都比较简单。

private Class> createAdaptiveExtensionClass() {

// 动态生成适配器类

String code = createAdaptiveExtensionClassCode();

ClassLoader classLoader = findClassLoader();

com.alibaba.dubbo.common.compiler.Compiler compiler =

ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

return compiler.compile(code, classLoader);

}

ExtensionLoader类中 createAdaptiveExtensionClassCode是一个代码拼装过程,这个方法就占了代理的1/3,代码就不全部贴了。

codeBuidler.append("package " + type.getPackage().getName() + ";");

codeBuidler.append("\nimport " + ExtensionLoader.class.getName() + ";");

codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adaptive" + " implements " + type.getCanonicalName() + " {");

//value代表@Adaptive注解中的参数,defaultExtName是@SPI注解参数

getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);

// 例如:

String getNameCode = String.format("url.getParameter(\"%s\")", "dubbo"); // 结果为url.getParameter("dubbo")

code.append("\nString extName = ").append(getNameCode).append(";");

// check extName == null?

String s = String.format("\nif(extName == null) " +

"throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",

type.getName(), Arrays.toString(value));

code.append(s);

s = String.format("\n%s extension = (%

type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());

code.append(s);

标签:dubbo,Dubbo,URL,适配,alibaba,url,interface,Adaptive,com

来源: https://www.cnblogs.com/caoxb/p/13140329.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值