2、Dubbo的SPI机制分析1

1、Dubbo的SPI例子

1@SPI 2public interface Robot { 3 void sayHello(); 4} 5 6public class OptimusPrime implements Robot{ 7 @Override 8 public void sayHello() { 9 System.out.println("Hello, I am Optimus Prime."); 10 } 11} 12 13public class Bumblebee implements Robot{ 14 @Override 15 public void sayHello() { 16 System.out.println("Hello, I am Bumblebee."); 17 } 18} 19 20 21public class DubboSPITest { 22 23 @Test 24 public void sayHelloDubbo() throws Exception { 25 ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); 26 Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); 27 optimusPrime.sayHello(); 28 Robot bumblebee = extensionLoader.getExtension("bumblebee"); 29 bumblebee.sayHello(); 30 } 31} 32 33 34输出: 35Hello, I am Optimus Prime. 36Hello, I am Bumblebee.

2、Dubbo的SPI源码分析

1ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); 2 3public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { 4 if (type == null) 5 throw new IllegalArgumentException("Extension type == null"); 6 if (!type.isInterface()) { 7 throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); 8 } 9 if (!withExtensionAnnotation(type)) { 10 throw new IllegalArgumentException("Extension type(" + type + 11 ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); 12 } 13 14 // 从缓存中获取与拓展类对应的ExtensionLoader 15 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 16 if (loader == null) { 17 // 若缓存未命中,则创建一个新的实例,先简单看下new ExtensionLoader<T>(type) 18 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 19 loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 20 } 21 return loader; 22}

这里的objectFactory创建可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入

1 private ExtensionLoader(Class<?> type) { 2 this.type = type; 3 // 这里的type是Robot.class,所以会执行后面的代码,加载并创建SpiExtensionFactory和SpringExtensionFactory, 4 objectFactory = (type == ExtensionFactory.class ? null : 5 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); 6} 7 8 9Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); 10 11 12public T getExtension(String name) { 13 if ("true".equals(name)) { 14 return getDefaultExtension(); 15 } 16 // Holder也是用于持有对象的,用作缓存 17 Holder<object> holder = cachedInstances.get(name); 18 if (holder == null) { 19 cachedInstances.putIfAbsent(name, new Holder<object>()); 20 holder = cachedInstances.get(name); 21 } 22 Object instance = holder.get(); 23 if (instance == null) { 24 synchronized (holder) { 25 instance = holder.get(); 26 if (instance == null) { 27 // 创建拓展实例,下面分析createExtension(name) 28 instance = createExtension(name); 29 holder.set(instance); 30 } 31 } 32 } 33 return (T) instance; 34}

这里依赖注入可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入

1private T createExtension(String name) { 2 // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表 3 // 加载完后根据name获取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime 4 // 待会重点分析getExtensionClasses(),删去了一些非关键代码 5 Class<?> clazz = getExtensionClasses().get(name); 6 try { 7 // 也是尝试先从缓存获取,获取不到通过反射创建一个并放到缓存中 8 T instance = (T) EXTENSION_INSTANCES.get(clazz); 9 if (instance == null) { 10 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 11 instance = (T) EXTENSION_INSTANCES.get(clazz); 12 } 13 // 依赖注入和cachedWrapperClasses,后面分析 14 injectExtension(instance); 15 Set<Class<?>> wrapperClasses = cachedWrapperClasses; 16 if (wrapperClasses != null && !wrapperClasses.isEmpty()) { 17 for (Class<?> wrapperClass : wrapperClasses) { 18 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 19 } 20 } 21 return instance; 22 } 23} 24 25 26 private Map<String, Class<?>> getExtensionClasses() { 27 // 从缓存中获取映射关系表 28 Map<string, class<?>> classes = cachedClasses.get(); 29 // 双重检查 30 if (classes == null) { 31 synchronized (cachedClasses) { 32 classes = cachedClasses.get(); 33 if (classes == null) { 34 // 若缓存中没有,去加载映射关系 35 classes = loadExtensionClasses(); 36 cachedClasses.set(classes); 37 } 38 } 39 } 40 return classes; 41} 42 43 44private Map<string, class<?>> loadExtensionClasses() { 45 // 获取SPI注解,这里的type是在调用getExtensionLoader方法时传入的 46 final SPI defaultAnnotation = type.getAnnotation(SPI.class); 47 if (defaultAnnotation != null) { 48 String value = defaultAnnotation.value(); 49 if ((value = value.trim()).length() > 0) { 50 String[] names = NAME_SEPARATOR.split(value); 51 if (names.length > 1) { 52 // 抛异常 53 } 54 // 获取@SPI注解中的值,并缓存起来,可以关注下,后面会用到 55 if (names.length == 1) cachedDefaultName = names[0]; 56 } 57 } 58 59 Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); 60 // 加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/ 61 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); 62 // 我是放在这个目录下的 63 loadDirectory(extensionClasses, DUBBO_DIRECTORY); 64 loadDirectory(extensionClasses, SERVICES_DIRECTORY); 65 return extensionClasses; 66} 67 68 69private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { 70 // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot 71 String fileName = dir + type.getName(); 72 try { 73 Enumeration<java.net.url> urls; 74 ClassLoader classLoader = findClassLoader(); 75 // 根据文件名加载所有同名文件 76 if (classLoader != null) { 77 urls = classLoader.getResources(fileName); 78 } else { 79 urls = ClassLoader.getSystemResources(fileName); 80 } 81 if (urls != null) { 82 while (urls.hasMoreElements()) { 83 // 一个resourceURL就是一个文件 84 java.net.URL resourceURL = urls.nextElement(); 85 loadResource(extensionClasses, classLoader, resourceURL); 86 } 87 } 88 } 89} 90 91 92private void loadResource(Map<string, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { 93 try { 94 BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); 95 try { 96 String line; 97 // 按行读取配置内容 98 while ((line = reader.readLine()) != null) { 99 final int ci = line.indexOf('#'); 100 // 定位#字符,#之后的为注释,跳过 101 if (ci >= 0) line = line.substring(0, ci); 102 line = line.trim(); 103 104 if (line.length() > 0) { 105 try { 106 String name = null; 107 // 按等号切割 108 int i = line.indexOf('='); 109 if (i > 0) { 110 name = line.substring(0, i).trim(); 111 line = line.substring(i + 1).trim(); 112 } 113 if (line.length() > 0) { 114 // 真正的去加载类 115 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); 116 } 117 } 118 } 119 } 120 } 121 } 122}

标注1,这里的可以参考: Dubbo的SPI机制分析2-Adaptive详解

标注2,这里的可以参考: Dubbo的SPI机制分析4-Dubbo通过Wrapper实现AOP

标注3,这里的cachedActivates可以参考: Dubbo的SPI机制分析5-Activate详解

1private void loadClass(Map<string, Class<?>> extensionClasses, java.net.URL resourceURL, 2 Class<?> clazz, String name) throws NoSuchMethodException { 3 // clazz必须是type类型的,否则抛异常 4 if (!type.isAssignableFrom(clazz)) { 5 } 6 // 判断clazz是否为标注了@Adaptive注解,标注1 7 if (clazz.isAnnotationPresent(Adaptive.class)) { 8 if (cachedAdaptiveClass == null) { 9 cachedAdaptiveClass = clazz; 10 } else if (!cachedAdaptiveClass.equals(clazz)) { 11 // 抛异常,不能有多个标注有@Adaptive注解的类 12 } 13 } 14 // 判断是否是Wrapper类型,标注2 15 else if (isWrapperClass(clazz)) { 16 Set<Class<?>> wrappers = cachedWrapperClasses; 17 if (wrappers == null) { 18 cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); 19 wrappers = cachedWrapperClasses; 20 } 21 wrappers.add(clazz); 22 } 23 24 // 程序进入此分支,表明clazz是一个普通的拓展类,Robot就是一个普通的拓展类 25 else { 26 // 检测clazz是否有默认的构造方法,如果没有,则抛出异常 27 clazz.getConstructor(); 28 if (name == null || name.length() == 0) { 29 name = findAnnotationName(clazz); 30 if (name.length() == 0) { 31 // 抛异常 32 } 33 } 34 35 String[] names = NAME_SEPARATOR.split(name); 36 if (names != null && names.length > 0) { 37 // 用于@Activate根据条件激活,标注3 38 Activate activate = clazz.getAnnotation(Activate.class); 39 if (activate != null) { 40 cachedActivates.put(names[0], activate); 41 } 42 for (String n : names) { 43 if (!cachedNames.containsKey(clazz)) { 44 cachedNames.put(clazz, n); 45 } 46 Class<?> c = extensionClasses.get(n); 47 if (c == null) { 48 // 存储名称到class的映射关系,这样就解析好了一行 49 extensionClasses.put(n, clazz); 50 } else if (c != clazz) { 51 // 抛异常 52 } 53 } 54 } 55 } 56}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )