Dubbo源码阅读(二)-Dubbo SPI机制

一、Java原生 SPI

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。

1、使用

在扩展类的 jar 包内,放置扩展点配置文件 META-INF/services/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

2、缺点:

a、接口的所有实现类全部都需要加载并实例化;
b、无法根据参数来指定实现类;
c、不能解决IOC、AOP的问题。

二、Dubbo SPI

1、使用

在扩展类的 jar 包内,放置扩展点配置文件 META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

2、自适应拓展点:@Adaptive
package com.alibaba.dubbo.rpc;
 
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
 
/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("dubbo")
public interface Protocol {
 
    int getDefaultPort();
    
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
 
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
 
    void destroy();
 
}

@Adaptive称为自适应扩展点注解,在实际应用场景中,一个扩展接口往往会有多种实现类,因为Dubbo是基于URL驱动,所以在运行时,通过传入URL中的某些参数来动态控制具体实现,这便是Dubbo的扩展点自适应特性。

以上面的Protocol接口为例,我们发现里面有两个方法export和refer,该方法上被@Adaptive修饰。
在运行的时候会针对 Protocol 生成代理类Protocol $ Adaptive代理类Protocol$Adaptive的export和refer方法的代码会在运行的时候动态根据 url 中的 protocol 来获取那个 key,默认是 dubbo,你也可以自己指定,你如果指定了别的 key,那么就会获取别的实现类的实例了,这就有点AOP的味道了。
Protocol $ Adaptive

import org.apache.dubbo.common.extension.ExtensionLoader;
 
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
	public void destroy() {
		throw new UnsupportedOperationException(
				"The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
	}
 
	public int getDefaultPort() {
		throw new UnsupportedOperationException(
				"The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
	}
 
	public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
			throws org.apache.dubbo.rpc.RpcException {
		if (arg0 == null)
			throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null)
			throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
		org.apache.dubbo.common.URL url = arg0.getUrl();
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
					+ url.toString() + ") use keys([protocol])");
		org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
				.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.export(arg0);
	}
 
	public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
			throws org.apache.dubbo.rpc.RpcException {
		if (arg1 == null)
			throw new IllegalArgumentException("url == null");
		org.apache.dubbo.common.URL url = arg1;
		
		// 根据URL的协议类型来指定拓展点,默认是dubbo
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
					+ url.toString() + ") use keys([protocol])");
		org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
				.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
}

所以Dubbo源码中,可以根据URL的协议类型来指定拓展点!这在源码阅读分析中很重要。

上一篇:linux设置服务器时间


下一篇:【故障处理】ORA-28040: No matching authentication protocol