SOFA RPC源码解析之扩展机制ExtensionLoader

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/beyondself_77/article/details/80862088

1.SOFA RPC源码解析

1.1  扩展机制

1.1.1  简述

        SOFA RPC的扩展点加载机制是从JDK标准的SPI扩展点发现机制加强而来。

        SPI,全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。

        SPI是上游产商给服务供应商提供的接口,供应商遵循接口契约提供自己的实现。供应商提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。当外部程序装配这个模块的时候,通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

        基于这样一个约定,可以很好的找到服务接口的实现类,而不需要再代码里制定。

        Java SPI机制的约定和使用方法如下图所示:

        Java SPI机制的约定:

        1.   在META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为API具体实现类的全限定名;

        2.   使用ServiceLoader类动态加载META-INF中的实现类;

        3.   如SPI的实现类为Jar,则需要放在主程序classPath中;

        4.   API具体实现类必须有一个不带参数的构造方法;

        简单地了解了Java SPI服务扩展机制以后,我们再看看SOFA RPC的扩展点加载机制的类图:

        SOFA RPC扩展机制的说明:

        1.   定义接口或抽象类,并在类上增加@Extensible注解;

        2.   定义接口或抽象类的具体实现类,并在类上增加@Extension注解;

        3.   在META-INF/services/sofa-rpc目录中,创建以接口全限定名命名的文件,例如:com.alipay.sofa.rpc.module.Module。该文件内容为key-value键值对,key为实现类别名,value为API具体实现类的全限定名,例如:fault-tolerance=com.alipay.sofa.rpc.module.FaultToleranceModule;

        4.  使用ExtensionLoaderFactory获取指定接口的ExtensionLoader实例,例如:ExtensionLoader<Module> loader = ExtensionLoaderFactory.getExtensionLoader(Module.class);

        5.   通过ExtensionLoader类getExtension(Stringalias)方法动态加载META-INF中的实现类对应的实例,例如loader.getAllExtensions(“fault-tolerance”);

        6.   如具体实现类为Jar,则需要放在主程序classPath中;

        7.   API具体实现类必须有一个不带参数的构造方法;

1.1.2 源码解析

        以加载模块接口Module的实现类为例,详细说明RPC中的扩展机制。

1.  ExtensionLoader<Module> loader =ExtensionLoaderFactory.getExtensionLoader(Module.class)
2.  loader.getAllExtensions()

        从上述代码可以看出,使用RPC扩展机制获取指定接口的具体实现类,主要包括两大步骤:

        一、  获取指定接口对应的ExtensionLoader

        从ExtensionLoaderFactory开始,该类是ExtensionLoader的工厂类,负责根据接口名称获取指定接口对应的ExtensionLoader。

1.  public static <T>ExtensionLoader<T> getExtensionLoader(Class<T> clazz,ExtensionLoaderListener<T> listener) {
2.          ExtensionLoader<T> loader =LOADER_MAP.get(clazz);
3.          if (loader == null) {
4.              synchronized (ExtensionLoaderFactory.class){
5.                  loader = LOADER_MAP.get(clazz);
6.                  if (loader == null) {
7.                      loader = new ExtensionLoader<T>(clazz,listener);
8.                      LOADER_MAP.put(clazz,loader);
9.                  }
10.             }
11.         }
12.         return loader;
13.     }

        getExtensionLoader方法涉及的主要变量描述:

        1.   listener:ExtensionLoaderListener接口的实现类,当扩展点加载时,可以做一些额外处理工作,例如解析code,初始化等操作;

        2.   clazz:含有@Extensible注解的接口;

        3.   LOADER_MAP:全局变量,缓存已经加载的指定接口的ExtensionLoader;

        首先,从LOADER_MAP获取指定接口的ExtensionLoader。如果存在,直接返回获取到的指定接口的ExtensionLoader;否则,创建新的ExtensionLoader;

1.  public ExtensionLoader(Class<T>interfaceClass, ExtensionLoaderListener<T> listener) {
2.          this(interfaceClass, true, listener);
3.      }

        调用ExtensionLoader的构造函数:

1.  protected ExtensionLoader(Class<T>interfaceClass, boolean autoLoad, ExtensionLoaderListener<T> listener) {
2.          ……
3.          // 接口为空,既不是接口,也不是抽象类
4.          if (interfaceClass == null ||
5.              !(interfaceClass.isInterface() ||Modifier.isAbstract(interfaceClass.getModifiers()))) {
6.              throw newIllegalArgumentException("Extensible class must be interface or abstractclass!");
7.          }
8.          this.interfaceClass = interfaceClass;
9.          this.interfaceName =ClassTypeUtils.getTypeStr(interfaceClass);
10.         this.listener = listener;
11.         Extensible extensible = interfaceClass.getAnnotation(Extensible.class);
12.         if (extensible == null) {
13.             throw new IllegalArgumentException(
14.                 "Error when loadextensible interface " + interfaceName + ", must add annotation@Extensible.");
15.         } else {
16.             this.extensible = extensible;
17.         }
18.  
19.         this.factory = extensible.singleton() ? newConcurrentHashMap<String, T>() : null;
20.         this.all = newConcurrentHashMap<String, ExtensionClass<T>>();
21.         if (autoLoad) {
22.             List<String> paths =RpcConfigs.getListValue(RpcOptions.EXTENSION_LOAD_PATH);
23.             for (String path : paths) {
24.                 loadFromFile(path);
25.             }
26.         }
27.     }

        ExtensionLoader构造函数的处理逻辑如下:

        1.   判断interfaceClass是否为接口或抽象类,如果不是,抛异常;

        2.   判断interfaceClass是否含有@Extensible注解,如果没有,抛异常;

        3.   判断注解@Extensible的singleton是否为true。如果为true,则表示扩展为单例模式,则创建factory,用于缓存已经创建的扩展的实例。否则,不创建factory,每次创建新的扩展实例返回。

        4.   通过RpcConfigs获取扩展的加载路径,META-INF/services/sofa-rpc/, META-INF/services/;

        5.   依次遍历加载路径,加载以接口全限定名命名的文件,如com.alipay.sofa.rpc.module.Module;

1.  protected synchronized voidloadFromFile(String path) {
2.          ……
3.          // 默认如果不指定文件名字,就是接口名
4.          String file =StringUtils.isBlank(extensible.file()) ? interfaceName :extensible.file().trim();
5.          String fullFileName = path + file;
6.          try {
7.              ClassLoader classLoader =ClassLoaderUtils.getClassLoader(getClass());
8.              loadFromClassLoader(classLoader,fullFileName);
9.          } catch (Throwable t) {
10.             ……
11.         }
12.     }

        获取当前的类加载器,例如:sun.misc.Launcher$AppClassLoader;

        通过类加载器加载当前类加载器路径上的所有com.alipay.sofa.rpc.module.Module文件以及父类加载器上的所有com.alipay.sofa.rpc.module.Module文件,包括classpath路径和所有该路径上的Jar包。

1.  protected voidloadFromClassLoader(ClassLoader classLoader, String fullFileName) throwsThrowable {
2.          Enumeration<URL> urls = classLoader !=null ? classLoader.getResources(fullFileName)
3.              :ClassLoader.getSystemResources(fullFileName);
4.          // 可能存在多个文件。
5.          if (urls != null) {
6.              while (urls.hasMoreElements()) {
7.                  // 读取一个文件
8.                  URL url = urls.nextElement();
9.                  ……
10.                 BufferedReader reader = null;
11.                 try {
12.                     reader = newBufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
13.                     String line;
14.                     while ((line = reader.readLine()) != null) {
15.                         readLine(url, line);
16.                     }
17.                 } catch (Throwable t) {
18.                    ……
19.                 } finally {
20.                  ……
21.  
22.                 }
23.             }
24.         }
25.     }

        加载所有的META-INF/services/sofa-rpc/com.alipay.sofa.rpc.module.Module文件,可能存在多个。针对每个文件,依次处理每个文件中的每行内容。例如:fault-tolerance=com.alipay.sofa.rpc.module.FaultToleranceModule;

1.  protected void readLine(URL url, Stringline) throws Throwable {
2.          String[] aliasAndClassName =parseAliasAndClassName(line);
3.          if (aliasAndClassName == null ||aliasAndClassName.length != 2) {
4.              return;
5.          }
6.          String alias = aliasAndClassName[0];
7.          String className = aliasAndClassName[1];
8.          // 读取配置的实现类
9.          Class tmp;
10.         try {
11.             tmp = ClassUtils.forName(className, false);
12.         } catch (Throwable e) {
13.             ……
14.             return;
15.         }
16.         if(!interfaceClass.isAssignableFrom(tmp)) {
17.             throw new IllegalArgumentException(……);
18.         }
19.         Class<? extends T> implClass =(Class<? extends T>) tmp;
20.  
21.         // 检查是否有可扩展标识
22.         Extension extension =implClass.getAnnotation(Extension.class);
23.         if (extension == null) {
24.             throw new IllegalArgumentException(……);
25.         } else {
26.             String aliasInCode =extension.value();
27.             if(StringUtils.isBlank(aliasInCode)) {
28.                 // 扩展实现类未配置@Extension 标签
29.                 throw new IllegalArgumentException(……);
30.             }
31.             if (alias == null) {
32.                 // spi文件里没配置,用代码里的
33.                 alias = aliasInCode;
34.             } else {
35.                 // spi文件里配置的和代码里的不一致
36.                 if (!aliasInCode.equals(alias)){
37.                     throw new IllegalArgumentException(……);
38.                 }
39.             }
40.             // 接口需要编号,实现类没设置
41.             if (extensible.coded() &&extension.code() < 0) {
42.                 throw newIllegalArgumentException(……);
43.             }
44.         }
45.         // 不可以是default*
46.         if (StringUtils.DEFAULT.equals(alias)|| StringUtils.ALL.equals(alias)) {
47.             throw new IllegalArgumentException(……);
48.         }
49.         // 检查是否有存在同名的
50.         ExtensionClass old = all.get(alias);
51.         ExtensionClass<T> extensionClass= null;
52.         if (old != null) {
53.             // 如果当前扩展可以覆盖其它同名扩展
54.             if (extension.override()) {
55.                 // 如果优先级还没有旧的高,则忽略
56.                 if (extension.order() <old.getOrder()) {
57.                     ……                } else {
58.                     ……                   
59.                 // 如果当前扩展可以覆盖其它同名扩展
60.                     extensionClass =buildClass(extension, implClass, alias);
61.                 }
62.             }
63.             // 如果旧扩展是可覆盖的
64.             else {
65.                 if (old.isOverride() &&old.getOrder() >= extension.order()) {
66.                     // 如果已加载覆盖扩展,再加载到原始扩展
67.                     ……
68.                 } else {
69.                     // 如果不能被覆盖,抛出已存在异常
70.                     throw newIllegalStateException(……);
71.                 }
72.             }
73.         } else {
74.             //如果不存在,则构造新的ExtensionClass
75.             extensionClass = buildClass(extension,implClass, alias);
76.         }
77.         if (extensionClass != null) {
78.             // 检查是否有互斥的扩展点
79.             for (Map.Entry<String,ExtensionClass<T>> entry : all.entrySet()) {
80.                 ExtensionClass existed =entry.getValue();
81.                 if (extensionClass.getOrder()>= existed.getOrder()) {
82.                     // 新的优先级 >= 老的优先级,检查新的扩展是否排除老的扩展
83.                     String[] rejection =extensionClass.getRejection();
84.                     if(CommonUtils.isNotEmpty(rejection)) {
85.                         for (String rej :rejection) {
86.                             ExtensionClassremoved = all.remove(rej);
87.                             ……
88.                         }
89.                     }
90.                 } else {
91.                     String[] rejection =existed.getRejection();
92.                     if(CommonUtils.isNotEmpty(rejection)) {
93.                         for (String rej :rejection) {
94.                             if(rej.equals(extensionClass.getAlias())) {
95.                                 // 被其它扩展排掉
96.                                 if(LOGGER.isInfoEnabled()) {
97.                                     ……
98.                                     return;
99.                                 }
100.                             }
101.                         }
102.                     }
103.                 }
104.             }
105.  
106.             loadSuccess(alias, extensionClass);
107.         }
108.     }

        依次处理读取到的每行内容,具体的处理逻辑参考上述注释,不在重复。

        其中,关键一步就是为指定接口的实现类创建新的ExtensionClass。

1.      private ExtensionClass<T>buildClass(Extension extension, Class<? extends T> implClass, Stringalias) {
2.          ExtensionClass<T> extensionClass = newExtensionClass<T>(implClass, alias);
3.          extensionClass.setCode(extension.code());
4.          extensionClass.setSingleton(extensible.singleton());
5.          extensionClass.setOrder(extension.order());
6.          extensionClass.setOverride(extension.override());
7.          extensionClass.setRejection(extension.rejection());
8.          return extensionClass;
9.      }

        最后,把创建的ExtensionClass增加到all中,然后通知监听器,增加了新的扩展类,做相应的处理。

1.      private void loadSuccess(String alias,ExtensionClass<T> extensionClass) {
2.          all.put(alias, extensionClass);
3.          if (listener != null) {
4.              listener.onLoad(extensionClass); // 加载完毕,通知监听器
5.          }
6.      }

        至此,扩展点加载器已经创建完成。

         二、    获取指定别名的扩展

        创建完指定接口的扩展点加载器以后,就是根据别名获取该接口的扩展实例。

1.  public T getExtension(String alias) {
2.          ExtensionClass<T> extensionClass =getExtensionClass(alias);
3.          if (extensionClass == null) {
4.              throw new SofaRpcRuntimeException(……);
5.          } else {
6.              if (extensible.singleton()&& factory != null) {
7.                  T t = factory.get(alias);
8.                  if (t == null) {
9.                      synchronized (this) {
10.                         t = factory.get(alias);
11.                         if (t == null) {
12.                             t = extensionClass.getExtInstance();
13.                             factory.put(alias,t);
14.                         }
15.                     }
16.                 }
17.                 return t;
18.             } else {
19.                 return extensionClass.getExtInstance();
20.             }
21.         }
22.     }

        1.   根据别名alias获取指定接口的扩展对应的ExtensionClass;

        2.   如果ExtensionClass为null,则直接抛异常。否则,继续执行下面的逻辑:

        3.   如果接口配置为单例,且factory不为空,则首先从factory获取,如果有,则直接返回。否则,调用ExtensionClass类getExtInstance方法创建扩展的实例,并缓存到factory,以便以后使用;

        4.   如果接口配置为非单例或factory为空,则直接调用ExtensionClass类getExtInstance方法创建扩展的实例,然后返回新创建的实例。

1.      public T getExtInstance(Class[] argTypes,Object[] args) {
2.          if (clazz != null) {
3.              try {
4.                  if (singleton) { // 如果是单例
5.                      if (instance == null) {
6.                          synchronized (this) {
7.                              if (instance ==null) {
8.                                  instance = ClassUtils.newInstanceWithArgs(clazz,argTypes, args);
9.                              }
10.                         }
11.                     }
12.                     return instance; // 保留单例
13.                 } else {
14.                     return ClassUtils.newInstanceWithArgs(clazz,argTypes, args);
15.                 }
16.             } catch (Exception e) {
17.                 throw newSofaRpcRuntimeException("create " + clazz.getCanonicalName() + "instance error", e);
18.             }
19.         }
20.         throw new SofaRpcRuntimeException("Classof ExtensionClass is null");
21.     }

        getExtInstance方法的主要处理逻辑如下:

        1. 判断ExtensionClass是否配置为单例模式,如果是,则判断instance是否为空,不为空,表示已经创建过该扩展的实例,直接返回。否则,调用ClassUtils类newInstanceWithArgs方法创建新的扩展的实例。

        2. 如果ExtensionClass配置为非单例模式,则直接调用ClassUtils类newInstanceWithArgs方法创建新的扩展的实例,并返回创建的实例。

1.  public static <T> TnewInstanceWithArgs(Class<T> clazz, Class<?>[] argTypes, Object[]args)
2.          throws SofaRpcRuntimeException {
3.          if (CommonUtils.isEmpty(argTypes)) {
4.              return newInstance(clazz);
5.          }
6.          try {
7.              if (!(clazz.isMemberClass()&& !Modifier.isStatic(clazz.getModifiers()))) {
8.                  Constructor<T>constructor = clazz.getDeclaredConstructor(argTypes);
9.                  constructor.setAccessible(true);
10.                 return constructor.newInstance(args);
11.             } else {
12.                 ……
13.                 if (constructor == null) {
14.                     throw newSofaRpcRuntimeException("The " + clazz.getCanonicalName()
15.                         + " has noconstructor with argTypes :" + Arrays.toString(argTypes));
16.                 } else {
17.                     constructor.setAccessible(true);
18.                     Object[] newArgs = newObject[args.length + 1];
19.                     System.arraycopy(args, 0,newArgs, 1, args.length);
20.                     return constructor.newInstance(newArgs);
21.                 }
22.             }
23.         } catch (SofaRpcRuntimeException e) {
24.             throw e;
25.         } catch (Throwable e) {
26.             throw newSofaRpcRuntimeException(e.getMessage(), e);
27.         }
28.     }

        最终,在ClassUtils类newInstanceWithArgs方法中,通过反射机制,调用指定类的构造函数,创建新的实例。

        至此,加载指定接口的扩展的实例已经完成。
展开阅读全文

没有更多推荐了,返回首页