SPI
java中的spi
- META-INF/services/接口全路径
- 写入实现类全路径
dubbo中的spi规范
- /META-INF/dubbo ;/META-INF/internal;/META-INF/services
- 文件名(全路径) 内容 key=value
ExtensionLoader
根据官网介绍,dubbo中支持各种领域的spi扩展,如:协议、监听、路由、负载集群等等。在扩展以后,dubbo中怎么去调用?怎么去区分在各种条件下的调用?
如下的代码,是调用关于prorocol的适配点
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
在源码中,有很多地方去引用这些可以扩展的点,都是使用如上的这个方法,去完成引用的。接下来看一下这个过程是怎么样的。
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ?
null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//上面3个做了校验
//1、type不能为空
//2、type不能为接口
//3、type需要有@spi的注解修饰
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
//使用一个map来保存已经加载过的loader和spring注册bean的方法相似
return loader;
}
上面代码中,都比较好理解,只有在构造方法中也许不太理解,接下来分析一下
//当去创建一个扩展点加载器时,会先加载ExtensionFactory的扩展点
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ?
null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
那么ExtensionLoader
- AdaptiveExtensionFactory
- SpiExtensionFactory
- SpringExtensionFactory
如下三种,怎么去加载。这里@Adaptive修饰了AdaptiveExtensionFactory,所以会去直接加载这个类。
接下来分析getAdaptiveExtension
public T getAdaptiveExtension() {
//如果已经加载过adaptive的时候,就不需要加载第二遍了
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
//双重检查,在dubbo中会遇到很多这样的双重检查。
if (instance == null) {
try {
//创建实例
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
private T createAdaptiveExtension() {
try {
//这里有2个方法很重要:getAdaptiveExtensionClass()和injectExtension(T instance)
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
这里分割一下,防止代码太多疲劳,下面先分析getAdaptiveExtensionClass()方法
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses(); //加载对应的class
//
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
//判断是否是扩展点
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
//加载一个默认的扩展点一般为使用 @SPI("xxx")
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
//loadDirectory方法中,主要是加载在配置文件中的类,根据dubbo的spi规则。需要注意的点为,如果发现在其中一个类上面,有一个@Adaptive注解,则直接把这个类,作为适配的类型返回,不参与下面的字节创建
//加载完后,创建字节码
private Class<?> createAdaptiveExtensionClass() {
//生成字节码
String code = createAdaptiveExtensionClassCode();
//class加载器
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);
}
所以getAdaptiveExtensionClass()该方法,是一个获得适配类的方法,同时根据加载代码中,可以得出如下分析
1、adaptive的注解如果修饰类,则当前类之间是适配类
2、adaptive的注解如果是修饰方法,则适配类是动态生成的
下面截取一个例子如下:类名为xxxx$Adaptive
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
在这里的代码就比较通俗了,extName
默认为dubbo,如果有传入则按传入的值走,到后面ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
。由于在生成字节码之前getExtensionClasses(); //加载对应的class
。所以在这里可以获取到DubboProtocol
的类型。所以这里虽然动态生成了一个类,但是最后调用的还是DubboProtocol的方法。
第二部分来看下,看过spring源码的都知道inject一般都是注入方法。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
上面的方法主要是通过反射,来注入属性,注入扩展点接口的属性,比如下面这个类
public class RegistryProtocol implements Protocol {
private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);
private static RegistryProtocol INSTANCE;
private final Map<URL, NotifyListener> overrideListeners = new ConcurrentHashMap<URL, NotifyListener>();
//To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
//providerurl <--> exporter
private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();
private Cluster cluster;
private Protocol protocol;
private RegistryFactory registryFactory;
private ProxyFactory proxyFactory;
。。。。。
其中cluster、protocol、proxyFactory、registryFactory都是spi扩展点,都可以通过injectExtension的方法来注入这些属性。通过objectFactory其查找需要注入的对象。如下代码
public AdaptiveExtensionFactory() {
//加载对应的工厂 spi和spring工厂
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);
}
public <T> T getExtension(Class<T> type, String name) {
//从spi和spring工厂中查找,如果有就直接返回
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
而这个RegistryProtocol的类和服务的发布有关系,下一节会介绍。