Dubbo扩展SPI源码剖析

ゝ一世哀愁。 2023-02-27 10:43 80阅读 0赞

题记:
文章内容输出来源:拉勾教育Java高薪训练营。
本篇文章是 Dubbo 学习课程中的一部分笔记。

Dubbo扩展SPI源码剖析

SPI在之前都有使用过,其中最重要的类就是 ExtensionLoader ,它是所有Dubbo中SPI的入口。
这里会具体介绍org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader 和 org.apache.dubbo.common.extension.ExtensionLoader.getExtension 方法。getExtensionLoader 获取扩展点加载器 并加载所对应的所有的扩展点实现,getExtension 根据name 获取扩展的指定实现。

一、getExtensionLoader 加载过程

(1)如何进行实例化 ExtensionLoader 的

  1. private static <T> boolean withExtensionAnnotation(Class<T> type) {
  2. // 包含`@SPI`注解在接口上
  3. return type.isAnnotationPresent(SPI.class);
  4. }
  5. public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
  6. // 必须传入类型
  7. if (type == null) {
  8. throw new IllegalArgumentException("Extension type null");
  9. }
  10. // 必须是接口类型
  11. if (!type.isInterface()) {
  12. throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
  13. }
  14. // 必须包含SPI的注解
  15. if (!withExtensionAnnotation(type)) {
  16. throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
  17. }
  18. // 尝试从已经加载过的数据中去读取(缓存功能)
  19. ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  20. if (loader == null) {
  21. // 如果没有的话,才会进行初始化,并且放入到缓存汇总
  22. EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
  23. loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  24. }
  25. return loader;
  26. }

(2)具体看一下 ExtensionLoader 的构造器函数, 这里他的实现比较简单,并没有做太多的操作。主要是对type进行赋值操作,然后获取 ExtensionFactory 对象。

  1. private ExtensionLoader(Class<?> type) {
  2. this.type = type;
  3. // 这里需要对对象的工厂做额外的创建,可以看到扩展的工厂也是一个扩展点
  4. objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
  5. }

(3)具体再来关注一下 ExtensionFactory 是做什么用的, 从这里我们可以大概的看出来,他是通过传入扩展点类型和真正的名称来获取扩展的。这里就和我们SPI中的具体名称实现相挂钩。

  1. @SPI
  2. public interface ExtensionFactory {
  3. /** * Get extension. * @param type object type. * @param name object name. * @return object instance. */
  4. <T> T getExtension(Class<T> type, String name);
  5. }

(4)可以在 dubbo-common/src/main/resources/META- INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory 中看到,他默认有三个实现的提供

  1. spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory
  2. adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
  3. spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory

(5)可以看到在 AdaptiveExtensionFactory 中是使用 @Adaptive 标记的。这里可以通过类名基本看出来,他其实最主要的作用是进行代理其他的ExtensionFactory。其中比较重要的方法在于getSupportedExtensions 方法,获取所有支持的扩展信息实现。

  1. @Adaptive
  2. public class AdaptiveExtensionFactory implements ExtensionFactory {
  3. private final List<ExtensionFactory> factories;
  4. public AdaptiveExtensionFactory() {
  5. // 获取针对ExtensionFactory扩展加载器
  6. ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
  7. List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
  8. // 获取支持的扩展
  9. for (String name : loader.getSupportedExtensions()) {
  10. // 将所有的ExtensionFactory进行缓存
  11. list.add(loader.getExtension(name));
  12. }
  13. factories = Collections.unmodifiableList(list);
  14. }
  15. @Override
  16. public <T> T getExtension(Class<T> type, String name) {
  17. for (ExtensionFactory factory : factories) {
  18. // 交给每个真实的ExtensionFactory来处理
  19. T extension = factory.getExtension(type, name);
  20. if (extension != null) {
  21. return extension;
  22. }
  23. }
  24. return null;
  25. }
  26. }

(6)获取所有支持的扩展信息实现: ExtensionLoader.getSupportedExtensions ,这里可以看到,其实比较关键的方法在于 getExtensionClasses 方法

  1. public Set<String> getSupportedExtensions() {
  2. // 获取所有的扩展类信息
  3. Map<String, Class<?>> clazzes = getExtensionClasses();
  4. // 返回所有的扩展点名称
  5. return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
  6. }

(7)观察 getExtensionClasses 的实现,可以看到这里其实主要做的就是一件事情,防止重复被加载,所以真正的的实现还需要专门去查看 loadExtensionClasses 方法。在我们通过名称获取扩展类之前,首先需要根据配置文件解析出扩展类名称到扩展类的映射关系表classes,之后再根据扩展项名称 从映射关系表中获取取对应的扩展类即可。相关过程代码分析如下

  1. private Map<String, Class<?>> getExtensionClasses() {
  2. // 从缓存中获取已加载的扩展类
  3. Map<String, Class<?>> classes = cachedClasses.get();
  4. // 双重检查
  5. if (classes == null) {
  6. // 为空的话,则锁住,标识只会被执行一次
  7. synchronized (cachedClasses) {
  8. classes = cachedClasses.get();
  9. if (classes == null) {
  10. // 进行加载信息 加载扩展类
  11. classes = loadExtensionClasses();
  12. cachedClasses.set(classes);
  13. }
  14. }
  15. }
  16. return classes;
  17. }

(8)观察 loadExtensionClasses 方法实现。这里主要做了两件事情。
1: 加载当前SPI的默认实现。
2:加载这个类的所有扩展点实现,并且按照name和Class对象的形式存储。
下面会专门针对于cacheDefaultExtensionName 和 loadDirectory 方法做说明

  1. private Map<String, Class<?>> loadExtensionClasses() {
  2. // 加载默认扩展的实现名称
  3. cacheDefaultExtensionName();
  4. // 获取其中每一种实现的名称和对应的classes
  5. // 具体的目录请参照下面的所有目录
  6. Map<String, Class<?>> extensionClasses = new HashMap<>();
  7. // internal extension load from ExtensionLoader's ClassLoader first
  8. loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
  9. loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);
  10. loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
  11. loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
  12. loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
  13. loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
  14. return extensionClasses;
  15. }

观察 cacheDefaultExtensionName 方法实现。这里面的是实现比较简单,主要用于读取注解中value值来获取到默认的名称。

  1. private void cacheDefaultExtensionName() {
  2. // 获取当前类是否包含SPI注解,一般走到这里都是拥有这个注解的
  3. final SPI defaultAnnotation = type.getAnnotation(SPI.class);
  4. if (defaultAnnotation == null) {
  5. return;
  6. }
  7. // 来获取其的value值,这个值主要的作用是设置这个SPI中的默认扩展名
  8. // 比如LoadBalance的默认实现就是random。就是通过这里进行的设置
  9. String value = defaultAnnotation.value();
  10. if ((value = value.trim()).length() > 0) {
  11. String[] names = NAME_SEPARATOR.split(value);
  12. if (names.length > 1) {
  13. throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names));
  14. }
  15. if (names.length 1) {
  16. cachedDefaultName = names[0];
  17. }
  18. }
  19. }

观察 loadDirectory 方法实现。这里的主要功能是从这个文件夹中寻找真正的文件列表,并且对其中的文件内容解析并且放入到 extensionClasses Map中,具体解析文件的内容实现,还要参考loadResource 实现。

  1. private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
  2. // 文件名称规则: 路径/包名.接口名
  3. String fileName = dir + type;
  4. try {
  5. // 寻找classloader和url列表
  6. Enumeration<java.net.URL> urls = null;
  7. ClassLoader classLoader = findClassLoader();
  8. // try to load from ExtensionLoader's ClassLoader first
  9. // 如果需要的话, 需要先从当前类的ClassLoader中寻找
  10. if (extensionLoaderClassLoaderFirst) {
  11. ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
  12. if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
  13. urls = extensionLoaderClassLoader.getResources(fileName);
  14. }
  15. }
  16. // 如果找不到任何的URL列表,则继续尝试去其当前线程的ClassLoader中寻找
  17. if(urls == null || !urls.hasMoreElements()) {
  18. if (classLoader != null) {
  19. urls = classLoader.getResources(fileName);
  20. } else {
  21. urls = ClassLoader.getSystemResources(fileName);
  22. }
  23. }
  24. // 如果存在文件的话
  25. if (urls != null) {
  26. while (urls.hasMoreElements()) {
  27. // 遍历每一个资源文件,并且进行加载资源信息到extensionClasses, 主要功能 是读取文件内容
  28. java.net.URL resourceURL = urls.nextElement();
  29. loadResource(extensionClasses, classLoader, resourceURL);
  30. }
  31. }
  32. } catch (Throwable t) {
  33. logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t);
  34. }
  35. }

(9)进行观察 loadResource 实现,主要是用于读取文件操作,并且将方法交由 loadClass 来加载类信息。加载类信息也是最重要的方法所在。

  1. private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
  2. try {
  3. // 读取文件
  4. try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
  5. String line;
  6. while ((line = reader.readLine()) != null) {
  7. // 截取文件#前面的内容
  8. final int ci = line.indexOf('#');
  9. if (ci >= 0) {
  10. line = line.substring(0, ci);
  11. }
  12. line = line.trim();
  13. // 如果有内容的话
  14. if (line.length() > 0) {
  15. try {
  16. // 则进行加载key=value的形式数据
  17. String name = null;
  18. int i = line.indexOf('=');
  19. if (i > 0) {
  20. name = line.substring(0, i).trim();
  21. line = line.substring(i + 1).trim();
  22. }
  23. if (line.length() > 0) {
  24. // 对类信息进行加载操作
  25. loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
  26. }
  27. } catch (Throwable t) {
  28. IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
  29. exceptions.put(line, e);
  30. }
  31. }
  32. }
  33. }
  34. } catch (Throwable t) {
  35. logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t);
  36. }
  37. }

(10)观察 loadClass 类的实现,可以看到这里是最终进行完成类映射的地方。关于Adaptive中的类实现原理,我们放在这个章节中的偏后面进行细讲。

  1. private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
  2. // 当前扩展点的实现,必须是当前扩展接口的实现才可以
  3. if (!type.isAssignableFrom(clazz)) {
  4. throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface.");
  5. }
  6. // 如果是包含了Adaptive注解,则认为是需要对扩展点包装的方法,这里只做了存储操作,存储至 cachedAdaptiveClass中
  7. if (clazz.isAnnotationPresent(Adaptive.class)) {
  8. cacheAdaptiveClass(clazz);
  9. } else if (isWrapperClass(clazz)) {
  10. // 判断是否是wapper类型, 是否构造函数中有该接口类型的传入
  11. // wrapper类型的意思是,对当前的扩展点实现封装功能处理
  12. cacheWrapperClass(clazz);
  13. } else {
  14. clazz.getConstructor();
  15. // 寻找他是否已经定义过了名称, 这里就不继续往里面细看了,主要是获取当前类的 org.apache.dubbo.common.Extension注解,如果有的话就使用这个名称,否则的话就是用当前类的 简单名称
  16. if (StringUtils.isEmpty(name)) {
  17. name = findAnnotationName(clazz);
  18. if (name.length() == 0) {
  19. throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
  20. }
  21. }
  22. // 否则的话,就对这个名称和class做映射关系
  23. String[] names = NAME_SEPARATOR.split(name);
  24. if (ArrayUtils.isNotEmpty(names)) {
  25. // 如果当前类拥有Activate注解,则将其进行添加到cachedActivates对象中,意味 着需要执行
  26. cacheActivateClass(clazz, names[0]);
  27. // 进行名称映射保存
  28. for (String n : names) {
  29. cacheName(clazz, n);
  30. saveInExtensionClass(extensionClasses, clazz, n);
  31. }
  32. }
  33. }
  34. }

当执行完这几个方法之后,会对一下几个字段进行更新:
cachedAdaptiveClass: 当前Extension类型对应的AdaptiveExtension类型(只能一个)
cachedWrapperClasses: 当前Extension类型对应的所有Wrapper实现类型(无顺序)
cachedActivates: 当前Extension实现自动激活实现缓存(map,无序)
cachedNames: 扩展点实现类对应的名称(如配置多个名称则值为第一个)

二、根据name获取扩展点的方法 getExtension

(1) getExtension 方法实现。这里面同样主要作用是根据name对扩展点进行处理和进行加锁来创建真实的引用,其中都是有使用缓存来处理。

  1. public T getExtension(String name) {
  2. if (StringUtils.isEmpty(name)) {
  3. throw new IllegalArgumentException("Extension name null");
  4. }
  5. // 获取当前SPi的默认扩展实现类
  6. if ("true".equals(name)) {
  7. return getDefaultExtension();
  8. }
  9. // 获取当前类的holder,实现原理和cachedClasses的方式相同,都是建立同一个引用后再进行 加锁
  10. final Holder<Object> holder = getOrCreateHolder(name);
  11. Object instance = holder.get();
  12. if (instance == null) {
  13. synchronized (holder) {
  14. instance = holder.get();
  15. if (instance == null) {
  16. // 真正进行创建实例
  17. instance = createExtension(name);
  18. holder.set(instance);
  19. }
  20. }
  21. }
  22. return (T) instance;
  23. }

(2)下面来看看 getOrCreateHolder 是如何保证缓存的。

  1. private Holder<Object> getOrCreateHolder(String name) {
  2. // 获取当前名称的和对象Holder的映射关系
  3. Holder<Object> holder = cachedInstances.get(name);
  4. if (holder == null) {
  5. // 如果不存在的话,则使用putIfAbsent的原子操作来设置值,这个值可以保证多线程的额情 况下有值的时候不处理,没有值进行保存
  6. cachedInstances.putIfAbsent(name, new Holder<>());
  7. // 获取真实的holder处理器
  8. holder = cachedInstances.get(name);
  9. }
  10. return holder;
  11. }

(3)然后我们再来看看 createExtension 的实现,他是具体根据扩展的class名称来进行创建实例的类。这里也是创建扩展点类的主要实现。下面我们也对其他扩展点注册的方法做说明。

  1. private T createExtension(String name) {
  2. // 从配置文件中加载所有的扩展类 可以得到配置项名称 到配置类的映射关系
  3. Class<?> clazz = getExtensionClasses().get(name);
  4. if (clazz == null) {
  5. throw findException(name);
  6. }
  7. try {
  8. // 获取是否已经有实例了
  9. T instance = (T) EXTENSION_INSTANCES.get(clazz);
  10. if (instance == null) {
  11. // 没有的话,同样适用putIfAbsent的方式来保证只会创建一个对象并且保存
  12. EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
  13. instance = (T) EXTENSION_INSTANCES.get(clazz);
  14. }
  15. // 注入其他扩展点的实体,用于扩展点和其他的扩展点相互打通
  16. injectExtension(instance);
  17. // 进行遍历所有的包装类信息,分别对包装的类进行包装实例化,并且返回自身引用
  18. Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  19. if (CollectionUtils.isNotEmpty(wrapperClasses)) {
  20. for (Class<?> wrapperClass : wrapperClasses) {
  21. // 同样进行注册其他扩展点的功能
  22. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
  23. }
  24. }
  25. // 对扩展点进行初始化操作
  26. initExtension(instance);
  27. return instance;
  28. } catch (Throwable t) {
  29. throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t);
  30. }
  31. }

(4) injectExtension 方法观察

  1. private T injectExtension(T instance) {
  2. if (objectFactory null) {
  3. return instance;
  4. }
  5. try {
  6. // 遍历其中的所有方法
  7. for (Method method : instance.getClass().getMethods()) {
  8. // 是否是set方法
  9. // 1. 以"set"开头
  10. // 2. 参数长度为1
  11. // 3. 是公开的方法
  12. if (!isSetter(method)) {
  13. continue;
  14. }
  15. /** * Check {@link DisableInject} to see if we need auto injection for this property */
  16. // 如果设置了取消注册,则不进行处理
  17. if (method.getAnnotation(DisableInject.class) != null) {
  18. continue;
  19. }
  20. // 获取参数类型,并且非基础类型(String, Integer等类型)
  21. Class<?> pt = method.getParameterTypes()[0];
  22. if (ReflectUtils.isPrimitives(pt)) {
  23. continue;
  24. }
  25. try {
  26. // 获取需要set的扩展点名称
  27. String property = getSetterProperty(method);
  28. // 从ExtensionLoader中加载指定的扩展点
  29. // 比如有一个方法为setRandom(LoadBalance loadBalance),那么则以为着需 要加载负载均衡中名为random的扩展点
  30. Object object = objectFactory.getExtension(pt, property);
  31. if (object != null) {
  32. method.invoke(instance, object);
  33. }
  34. } catch (Exception e) {
  35. logger.error("Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e);
  36. }
  37. }
  38. } catch (Exception e) {
  39. logger.error(e.getMessage(), e);
  40. }
  41. return instance;
  42. }

三、Adaptive功能实现原理

Adaptive的主要功能是对所有的扩展点进行封装为一个类,通过URL传入参数的时动态选择需要使用的扩展点。其底层的实现原理就是动态代理,这里我们会通过源码的形式告诉大家,他是如何通过动态代理进行加载的。
(1)这里我们 getAdaptiveExtension 方法讲起,这个里面就是真正获取该类。这里可以看到,ExtentionLoader 中大量的使用了Holder和加锁的方式去进行唯一创建。

  1. public T getAdaptiveExtension() {
  2. // 和原先是用相同的方式,进行Holder和加锁的方式来保证只会被创建一次
  3. Object instance = cachedAdaptiveInstance.get();
  4. if (instance == null) {
  5. // 如果直接已经有创建并且错误的情况,则直接返回错误信息,防止重复没必要的创建
  6. if (createAdaptiveInstanceError != null) {
  7. throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
  8. }
  9. synchronized (cachedAdaptiveInstance) {
  10. instance = cachedAdaptiveInstance.get();
  11. if (instance == null) {
  12. try {
  13. // 这里真实的进行创建操作
  14. instance = createAdaptiveExtension();
  15. cachedAdaptiveInstance.set(instance);
  16. } catch (Throwable t) {
  17. createAdaptiveInstanceError = t;
  18. throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
  19. }
  20. }
  21. }
  22. }
  23. return (T) instance;
  24. }

(2)这里我们继续从 createAdaptiveExtension 来去查看实现。这里主要是进行了一些方法封装。

  1. private T createAdaptiveExtension() {
  2. try {
  3. // 这里使用`getAdaptiveExtensionClass`方法进行构建类并且执行实例化
  4. // 然后和普通的其他class相同,依旧使用injectExtension进行扩展
  5. return injectExtension((T) getAdaptiveExtensionClass().newInstance());
  6. } catch (Exception e) {
  7. throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
  8. }
  9. }
  10. private Class<?> getAdaptiveExtensionClass() {
  11. // 确保已经加载了所有的扩展类信息
  12. getExtensionClasses();
  13. // 如果已经加载过了,则直接返回
  14. if (cachedAdaptiveClass != null) {
  15. return cachedAdaptiveClass;
  16. }
  17. // 否则进行构建操作
  18. return cachedAdaptiveClass = createAdaptiveExtensionClass();
  19. }

(3)具体再来看 createAdaptiveExtensionClass 方法。这里主要是进行生成Adaptive的代码,并且进行编译生成class。

  1. private Class<?> createAdaptiveExtensionClass() {
  2. // 实例化一个新的Adaptive的代码生成器,并且进行代码生成
  3. String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
  4. // 获取类加载器
  5. ClassLoader classLoader = findClassLoader();
  6. // 通过扩展点,寻找编译器, 目前有Java自带的编译器和Javassist的编译器,这里不做细展开
  7. org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.cla ss).getAdaptiveExtension();
  8. // 编译并且生成class
  9. return compiler.compile(code, classLoader);
  10. }

(4)具体通过 AdaptiveClassLoaderCodeGenerator.generate 方法来进行实现真正的代码生成。

  1. public String generate() {
  2. // 如果没有任何方法标记为Adaptive,则不做处理
  3. if (!hasAdaptiveMethod()) {
  4. throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
  5. }
  6. // 进行编写代码
  7. StringBuilder code = new StringBuilder();
  8. // 生成包信息
  9. code.append(generatePackageInfo());
  10. // 生成引用信息
  11. code.append(generateImports());
  12. // 生成类声明
  13. code.append(generateClassDeclaration());
  14. // 生成每一个方法
  15. Method[] methods = type.getMethods();
  16. for (Method method : methods) {
  17. code.append(generateMethod(method));
  18. }
  19. // 输出最后的一个"}"来结束当前类
  20. code.append("}");
  21. if (logger.isDebugEnabled()) {
  22. logger.debug(code.toString());
  23. }
  24. return code.toString();
  25. }

(5)这里主要对其中的每一个方法来做处理。具体主要观看 generateMethod 方法。这里的很多方法主要是依赖反射机制去进行方法封装,最终拼接为一个最终字符串。其中最关键的方法在于generateMethodContent 方法来生成代理功能。

  1. private String generateMethod(Method method) {
  2. // 方法返回类型
  3. String methodReturnType = method.getReturnType().getCanonicalName();
  4. // 方法名称
  5. String methodName = method.getName();
  6. // 生成方法内容
  7. String methodContent = generateMethodContent(method);
  8. // 生辰参数列表
  9. String methodArgs = generateMethodArguments(method);
  10. // 方法抛出的异常
  11. String methodThrows = generateMethodThrows(method);
  12. // 格式化为一个字符串
  13. return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
  14. }

(6) generateMethodContent 方法解读。这块儿更推荐通过debug的形式走进来, 看代码也更直接了当(就可以直接按照常用功能中的SPI章节来debug)。这部分也是整个Adaptive中最为核心的代码,包括获取扩展点名称并且执行。

  1. private String generateMethodContent(Method method) {
  2. // 获取Adaptive注解,只支持含有Adaptive注解方法处理
  3. Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
  4. StringBuilder code = new StringBuilder(512);
  5. if (adaptiveAnnotation == null) {
  6. // 没有该注解,直接抛出异常
  7. // throw new UnsupportedOperationException
  8. return generateUnsupported(method);
  9. } else {
  10. // 获取URL参数的所在位置
  11. int urlTypeIndex = getUrlTypeIndex(method);
  12. if (urlTypeIndex != -1) {
  13. // 增加判断url不为空的代码
  14. code.append(generateUrlNullCheck(urlTypeIndex));
  15. } else {
  16. // 获取这个方法中的所有参数列表
  17. // 寻找每个参数中是否有"get"开头的方法,并且返回值是URL的
  18. // 如果有则同样认定为找到,否则抛出异常
  19. code.append(generateUrlAssignmentIndirectly(method));
  20. }
  21. // 获取扩展点的适配名称
  22. String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
  23. // 判断是否有参数是Invocation类
  24. // 这里判断的主要目的在于,拥有Invocation时,则获取扩展名称的方式发生改变
  25. // 存在Invocation时,通过getMethodParameter,否则通过getParameter来执行
  26. // getMethodParameter是dubboURL中特有的,用于将"test.a"转换为"testA"的形式
  27. boolean hasInvocation = hasInvocationArgument(method);
  28. // 增加有Invocation类时的不为空判断
  29. code.append(generateInvocationArgumentNullCheck(method));
  30. // 生成获取扩展点名称的方法
  31. code.append(generateExtNameAssignment(value, hasInvocation));
  32. // 检查扩展点不能为空
  33. code.append(generateExtNameNullCheck(value));
  34. // 获取扩展点实现
  35. code.append(generateExtensionAssignment());
  36. // 返回扩展点中的真实调用
  37. code.append(generateReturnAndInvocation(method));
  38. }
  39. return code.toString();
  40. }

至此,结束


最后

在这个知识付费的时代,每一位热爱技术分享、奋笔直书的人,都值得我们尊敬!所以,请不要吝啬您手中的鼠标,按下左键,为小编点个赞吧。
更多内容,请关注微信公众号:架构视角

特别鸣谢

感谢程道老师风趣幽默的讲解,让我对所学知识点记忆深刻!
感谢木槿导师的认真和负责,每一次作业点评都是我前进的动力!
感谢班主任毕老师的负责和耐心,每次不厌其烦的上课通知都是我不忘初心,保持良好学习状态的精神支柱!
感谢拉勾教育平台,给我这次花少量的钱就能报名第一期拉钩训练营,就能学习到很多深层次的技术精华的机会。而且,在学习过程中还认识了很多技术大佬,可以请教他们一些问题,比如张大佬、卢大佬、雨生大佬等等。。

发表评论

表情:
评论列表 (有 0 条评论,80人围观)

还没有评论,来说两句吧...

相关阅读

    相关 dubbo解析-spi(五)

    Dubbo源码解析系列文章均来自肥朝简书 前言 之前对dubbo的`SPI`进行了四篇的分享.大家对这个概念有了一些初步的了解.谈到编程水平如何进阶,大家可能都会异口同