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方法中,通过反射机制,调用指定类的构造函数,创建新的实例。