dubbo源码分析(一)SPI 置顶! 有更新!

Published on with 58 views

dubbo spi

dubbo为什么没有采用JDK的SPI

  1. JDK的SPI在初始化时会加载所有的配置实现类,初始化耗时。
  2. 没有IOC和AOP的支持。

dubbo spi约定

  1. 存储路径: META-INF/dubbo/internal目录下,文件名是包名加类名。
  2. 配置文件的内容格式为:扩展名=具体实现类,通过key进行有需加载。

dubbo spi实现

实现类:ExtensionLoader

实现路径:

  1. getExtensionLoader(clazz)为该接口创建一个ExtensionLoader,然后缓存起来。
  2. getAdaptiveExtension()获取一个扩展装饰类的对象。
  3. getExtension(extName)获取实现对象。

ExtensionLoader构造函数:

 private ExtensionLoader(Class
       type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); } 

type是接口的类型,objectFactory还是通过ExtensionLoader.getExtensionLoader(xxxx)方法获取,不过赋值的是该extensionLoadergetAdaptiveExtension()方法的返回值。提为dubbo的IOC提供所有的对象,对于ExtensionFactory对象来说,其objectFactorynull

ExtensionLoader.getExtensionLoader(Class type)

 public static 
      
        ExtensionLoader
       
         getExtensionLoader(Class
        
          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
         
           loader = (ExtensionLoader
          
           ) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader
           
            (type)); loader = (ExtensionLoader
            
             ) EXTENSION_LOADERS.get(type); } return loader; } 
            
           
          
         
        
       
      

先从缓存中获取,如果有直接返回,如果没有则创建并存入到缓存中,缓存名为EXTENSION_LOADERS,是一个ConcurrentHashMap

ExtensionLoader.getAdaptiveExtension()

核心代码逻辑如下:

-->getAdaptiveExtension()//为cachedAdaptiveInstance赋值 -->createAdaptiveExtension() -->getAdaptiveExtensionClass() -->getExtensionClasses()//为cachedClasses 赋值 -->loadExtensionClasses() -->loadFile -->createAdaptiveExtensionClass()//自动生成和编译一个动态的adpative类,这个类是一个代理类 -->ExtensionLoader.getExtensionLwww.jmhat.com).getAdaptiveExtension() -->www.jmhat.com(code, classLoader) -->injectExtension()//作用:进入IOC的反转控制模式,实现了动态注入 

其中核心流程为createAdaptiveExtensionClass()injectExtension(),分别实现了生成、编译动态代理类和ioc依赖动态注入。

其中生成动态类的核心代码为ExtensionLoader.createAdaptiveExtensionClassCode(),源码如下:

 private String createAdaptiveExtensionClassCode() { StringBuilder codeBuilder = new StringBuilder(); Method[] methods = type.getMethods(); boolean hasAdaptiveAnnotation = false; for (Method m : methods) { if (m.isAnnotationPresent(Adaptive.class)) { hasAdaptiveAnnotation = true; break; } } // no need to generate adaptive class since there's no adaptive method found. if (!hasAdaptiveAnnotation) throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!"); codeBuilder.append("package ").append(type.getPackage().getName()).append(";"); codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";"); codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {"); for (Method method : methods) { Class
       rt = method.getReturnType(); Class
      [] pts = method.getParameterTypes(); Class
      [] ets = method.getExceptionTypes(); Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); if (adaptiveAnnotation == null) { code.append("throw new UnsupportedOperationException(\"method ") .append(method.toString()).append(" of interface ") .append(type.getName()).append(" is not adaptive method!\");"); } else { int urlTypeIndex = -1; for (int i = 0; i < pts.length; ++i) { if (pts[i].equals(URL.class)) { urlTypeIndex = i; break; } } // found parameter in URL type if (urlTypeIndex != -1) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");", urlTypeIndex); code.append(s); s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex); code.append(s); } // did not find parameter in URL type else { String attribMethod = null; // find URL getter method LBL_PTS: for (int i = 0; i < pts.length; ++i) { Method[] ms = pts[i].getMethods(); for (Method m : ms) { String name = m.getName(); if ((name.startsWith("get") || name.length() > 3) && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) && m.getParameterTypes().length == 0 && m.getReturnType() == URL.class) { urlTypeIndex = i; attribMethod = name; break LBL_PTS; } } } if (attribMethod == null) { throw new IllegalStateException("fail to create adaptive class for interface " + type.getName() + ": not found url parameter or url attribute in parameters of method " + method.getName()); } // Null point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");", urlTypeIndex, pts[urlTypeIndex].getName()); code.append(s); s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");", urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod); code.append(s); s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod); code.append(s); } String[] value = adaptiveAnnotation.value(); // value is not set, use the value generated from class name as the key if (value.length == 0) { char[] charArray = type.getSimpleName().toCharArray(); StringBuilder sb = new StringBuilder(128); for (int i = 0; i < charArray.length; i++) { if (Character.isUpperCase(charArray[i])) { if (i != 0) { sb.append("."); } sb.append(Character.toLowerCase(charArray[i])); } else { sb.append(charArray[i]); } } value = new String[]{sb.toString()}; } boolean hasInvocation = false; for (int i = 0; i < pts.length; ++i) { if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i); code.append(s); s = String.format("\nString methodName = arg%d.getMethodName();", i); code.append(s); hasInvocation = true; break; } } String defaultExtName = cachedDefaultName; String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if (i == value.length - 1) { if (null != defaultExtName) { if (!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } else { if (!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\")", value[i]); else getNameCode = "url.getProtocol()"; } } else { if (!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); else getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } 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 = (%
      
        0) { codeBuilder.append(", "); } codeBuilder.append(pts[i].getCanonicalName()); codeBuilder.append(" "); codeBuilder.append("arg").append(i); } codeBuilder.append(")"); if (ets.length > 0) { codeBuilder.append(" throws "); for (int i = 0; i < ets.length; i++) { if (i > 0) { codeBuilder.append(", "); } codeBuilder.append(ets[i].getCanonicalName()); } } codeBuilder.append(" {"); codeBuilder.append(code.toString()); codeBuilder.append("\n}"); } codeBuilder.append("\n}"); if (logger.isDebugEnabled()) { logger.debug(codeBuilder.toString()); } return codeBuilder.toString(); } 
      

其中处理流程为:
1. 构建包名类名等类的基础信息。
2. 遍历类的所有方法找出带有@Adaptive注解的方法,只处理带有注解的方法,不带有注解的方法则写入默认抛出异常的实现代码。
3. 处理带有@Adaptive的方法时,先尝试是否能找到URL类型的参数,如果不能则尝试在当前类的方法中找到看有没有get开头返回类型为URL的方法,如果都没有的话,则会加入抛出失败创建动态类的默认实现。
4. 构建获取extNamevalue,如果@Adaptive注解中没有赋值的话,则value中会加入当前类名小写的默认value
5. 判断是否含有Invocation的参数。
6. 获取extName。首先判断value是否是Protocol,如果是的话直接通过url.getProtocol()进行获取,如果不是接下来判断是否包含Invocation,如果包含的话通过url.getMethodParameter()获取,如果不是的话通过url.getParameter()获取,默认值为cachedDefaultName(@spi注解的value)。
7. 通过getExtensionLoader(class).getExtension(extName)获取extension
8. 通过获取到的extension调用该方法。

其中实现依赖注入的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())) { /** * Check {@link DisableInject} to see if we need auto injection for this property */ if (method.getAnnotation(DisableInject.class) != null) { continue; } 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; } 

其中核心处理逻辑为通过set解析到属性名,然后尝试去objectFactory中尝试获取对象,如果获取到对象的话,则通过反射调用该set方法进行赋值,objectFactory会遍历所有的ExtensionFactory,其中默认为[SpiExtensionFactory,SpringExtensionFactory]

ExtensionLoader.getExtension()

核心代码逻辑如下:

getExtension(String name) //指定对象缓存在cachedInstances;get出来的对象wrapper对象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一个。 -->createExtension(String name) -->getExtensionClasses() -->injectExtension(T instance)//dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值。 -->objectFactory.getExtension(pt, property) -->SpiExtensionFactory.getExtension(type, name) -->ExtensionLoader.getExtensionLoader(type) -->loader.getAdaptiveExtension() -->SpringExtensionFactory.getExtension(type, name) -->context.getBean(name) -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的简单设计 

其中ioc的实现在ExtensionLoader.getAdaptiveExtension()已有说明,其中aop的简单实现为遍历cachedWrapperClasses中的所有Wrapper,通过injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));进行包装。

@Adaptive注解

@adaptive设计的目的是为了识别固定已知类和扩展未知类。

注解在类和方法上的区别:

注解在类上:代表人工实现,实现一个装饰类(设计模式中的装饰模式),它主要作用于固定已知类,目前整个系统只有2个,AdaptiveCompilerAdaptiveExtensionFactory
- 为什么AdaptiveCompiler这个类是固定已知的?因为整个框架仅支持JavassistJdkCompiler
- 为什么AdaptiveExtensionFactory这个类是固定已知的?因为整个框架仅支持2个objFactory,一个是spi,另一个是spring

注解在方法上:代表自动生成和编译一个动态的Adpative类,它主要是用于SPI,因为spi的类是不固定、未知的扩展类,所以设计了动态$Adaptive类。

Responses