dubbo 中SPI 拓展这一节中,@Adaptive是相当重要的一部分,ExtensionLoader构造器中会调用getAdaptiveExtension()方法触发为当前扩展类型生成适配器,用到的是动态代理技术。dubbo中是通过javassist技术生成的动态代理类。与传统的jdk动态代理、cglib不同,javassist提供封装后的API对字节码进行间接操作,简单易用,不关心具体字节码,灵活性更高,且处理效率也较高,是dubbo默认的编译器。
在之前的推文中我们知道,dubbo有很多SPI的拓展点,而ExtensionLoader又是dubbo SPI拓展点的加载器。这篇文章中我们将以ExtensionLoader为切入点来对dubbo的SPI机制进行分析。dubbo中的SPI拓展点的主要位置在:
在META-INF/dubbo/internal/目录里有主要有:
在com.alibaba.dubbo.config.ServiceConfig类中有这样几个属性:
这里是通过拓展点加载器ExtensionLoader来加载Protocol和ProxyFactory,我们来针对Protocol为例来看一看具体流程: Protocol类的定义为:
Protocol的spi拓展文件如下:
在它的export和refer方法上都加了@Adaptive注解。下面进行具体分析。
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!"); } 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); } return loader; }
这里是获取(先在缓存中取,如果缓存中取不到就新建,然后放入缓存并返回)指定type的ExtensionLoader的代码。
public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if(createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) {//double check 获取protocol 类对应的adaptiveExtension实例(先取缓存,缓存中没有则新建并放入缓存和返回) instance = cachedAdaptiveInstance.get(); 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; }
这一段是double check 获取protocol 类对应的adaptiveExtension实例(先取缓存,缓存中没有则新建并放入缓存和返回),在SPI介绍的那一篇有提到过这个。下面我们看一下createAdaptiveExtension(),因为第一次进来肯定是要走这个方法的:
private T createAdaptiveExtension() { try { //IOC return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); }}
将getAdaptiveExtensionClass().newInstance()生成的实例通过injectExtension进行注入(IOC)。这里注入的是根据cachedAdaptiveClass生成的实例,请关注下文对cachedAdaptiveClass的说明。进入getAdaptiveExtensionClass方法:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses();//加载拓展文件中的classes if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass();//创建AdaptiveExtensionClass}
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; }
double check 加载classes的过程,接下来看一看loadExtensionClasses()的实现:
private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";// 此方法已经getExtensionClasses方法同步过。 private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { String value = defaultAnnotation.value(); if(value != null && (value = value.trim()).length() > 0) { 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<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
这个方法主要从上面三个spi目录中加载文件,并将class文件分类放好,具体分类的过程在loadFile方法中:
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { String fileName = dir + type.getName();//会从目录的type.getName对应的目录中去加载对应的文件 try { Enumeration<java.net.URL> urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL url = urls.nextElement(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); try { String line = null; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) line = line.substring(0, ci); line = line.trim(); if (line.length() > 0) { try { String name = null; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { Class<?> clazz = Class.forName(line, true, classLoader); if (! type.isAssignableFrom(clazz)) {//如果加载到clazz不是type的子类,则抛异常 throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) {//如果类上面加了@Adaptive注解,则将这个类设置成cachedAdaptiveClass if(cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (! cachedAdaptiveClass.equals(clazz)) {//如果这个clazz和之前设置的cachedAdaptiveClass不一致则报错。也就是说一个像Protocol这样的父接口,它的实现类上只能有一个加@Adaptive注解 throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } else { try { clazz.getConstructor(type);//如果类上面没有加@Adaptive注解,则尝试获取这个类上有无带有父接口(type)类型的参数的构造方法,如果有那么这个实现类就属于使用装饰器模式装饰过的wrappers,放入cachedWrapperClasses中去 Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { //没有对应的构造方法,则调用无参的构造方法 clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name == null || name.length() == 0) { //实现类的className要比type的大 if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); } else { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); } } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { //如果加了Activate注解则放入cachedActivates目录中 Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (! cachedNames.containsKey(clazz)) { //如果没有Activate注解则放入cachedNames cachedNames.put(clazz, n); } Class<?> c = extensionClasses.get(n); if (c == null) { //放入extensionClasses中去 extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } } } } } } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } // end of while read lines } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + url + ") in " + url, t); } } // end of while urls } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
*/
它的三个实现类:只有AdaptiveExtensionFactory是加了@Adaptive注解的,注意这里最后也是调的loader的getExtension方法(关于这个方法见下文有详细描述)。
这种情况下在上面的getAdaptiveExtensionClass方法中就会走cachedAdaptiveClass != null的分支,直接返回cachedAdaptiveClass, 也就是说生成的ExtensionFactory实例是AdaptiveExtensionFactory。如果没有实现类上加有@Adaptive注解,则会走下面的方法,使用父接口去创建Adaptive,如Protocol接口:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses();//加载拓展文件中的classes if (cachedAdaptiveClass != null) {//不为null直接返回 return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass();//创建AdaptiveExtensionClass }
不同的是对于wrapperClasses这里会使用有参构造的方式进行构建实例。接下来loadExtensionClasses方法是上面分析过的。这就是表明这里是将加载到的classes进行缓存,然后在getExtension方法的时候使用。
private Class<?> createAdaptiveExtensionClass() { String code = createAdaptiveExtensionClassCode();//要生成的adaptive类的代码 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); }
关于Compiler:
com.alibaba.dubbo.common.extension.ExtensionLoader#getDefaultExtension方法:
/** * 返回缺省的扩展,如果没有设置则返回<code>null</code>。 */ public T getDefaultExtension() { getExtensionClasses(); if(null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); }
关于cachedDefaultName:
可见,默认用的是javassist compiler。
对于createAdaptiveExtensionClassCode方法的作用主要是生成adaptive代理类的代码,然后交给Compiler进行编译生成class文件,它的部分代码如下:
package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { // 没有打上@Adaptive的方法如果被调到抛异常 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!"); } // 没有打上@Adaptive的方法如果被调到抛异常 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!"); } // 接口中export方法打上@Adaptive注册 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)//url属性不能为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());//从url中获取到实际使用的拓展点的name,也就是META-INF目录下配置的kv中的key值 if (extName == null)//extentsion的名称不能为null throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); // 利用dubbo服务查找机制根据名称找到具体的扩展点实现 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); } 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());//从url中获取到实际使用的拓展点的name,也就是META-INF目录下文件中配置的kv中的key值 if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); //通过extName获取对应的拓展点 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); }}
extName是实际需要调用的拓展点的名称,然后可以看到在$Adaptive类中最终是使用getExtension方法来获取真正使用的拓展点的,关于getExtension方法在上面有详细的讲解。
这时候可以回过来看一看com.alibaba.dubbo.config.ServiceConfig中的几个方法,以exportLocal方法为例:
private void exportLocal(URL url) { if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(NetUtils.LOCALHOST) .setPort(0); // modified by lishen ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref)); Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry"); } }
在构建invoker时url和extName都是事先处理好的,这个不清楚的可以去看我写的另一篇文章:dubbo源码之Proxy、Transporter和Exchanger执行过程。
到这里再回过头来看com.alibaba.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension方法:
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); }}
上面通过getAdaptiveExtensionClass()获得到了对应的Adaptive的代理的大Class对象,然后通过newInstance方法生成一个实例,再通过injectExtension方法进行注入。
关于injectExtension:
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())) {// 处理所有set方法 Class<?> pt = method.getParameterTypes()[0];// 获取set方法参数类型 try { // 获取setter对应的property名称 String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); // 根据类型,名称信息从ExtensionFactory获取,实际上是AdaptiveExtensionFactory if (object != null) { // 如果不为null,说set方法的参数是扩展点类型,那么进行注入 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;}
该方法的作用类似于spring中的ioc功能,也就是对一个bean 实例进行属性的注入,但是这个属性需要满足一定的条件:有规范的set方法,然后必须是拓展点类型的属性(在extensionFactory中要能找到)。