前面讲了Java的spi机制,但是Java的spi并不能实现按需加载,它会加载接口的所有实现类,因此Dubbo并未使用Java的spi而是自己实现了一套SPI机制,将逻辑封装在ExtensionLoader中,实现按需加载
Dubbo spi示例
首先创建接口:
@SPI // 注意需要加上该注解 public interface SPIService { void sayHello(); } 复制代码
创建接口实现类:
public class SPIServiceImplOne implements SPIService { @Override public void sayHello() { System.out.println("你好,我是实现类1"); } } public class SPIServiceImplTwo implements SPIService { @Override public void sayHello() { System.out.println("你好,我是实现类2"); } } 复制代码
在resource的META-INF下创建dubbo文件夹,并创建接口全限定名的文件:
META-INF/dubbo/com.lgx.dubbo.spi.SPIService
SPIServiceImplOne=com.lgx.dubbo.spi.impl.SPIServiceImplOne SPIServiceImplTwo=com.lgx.dubbo.spi.impl.SPIServiceImplTwo 复制代码
接下来进行测试:
@Test public void testDubboSPI(){ /** * 获取ExtensionLoader (class到获取ExtensionLoader的映射保存到map中) */ ExtensionLoader<SPIService> extensionLoader = ExtensionLoader.getExtensionLoader(SPIService.class); /** * 创建扩展点 * 1. 通过getExtensionClasses 在指定目录下获取所有的扩展类(配置项名称”到“配置类”的映射保存在map中) * 2. 通过反射创建扩展对象 clazz.newInstance * 3. 向扩展对象中注入依赖 IOC * 4. 将扩展对象包裹在相应的Wrapper对象中 AOP */ SPIService loaderOne = extensionLoader.getExtension("SPIServiceImplOne"); loaderOne.sayHello(); SPIService loaderTwo = extensionLoader.getExtension("SPIServiceImplTwo"); loaderTwo.sayHello(); } 输出结果: 你好,我是实现类1 你好,我是实现类2 复制代码
Dubbo spi的实现原理
首先看第一行:
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(SPIService.class);
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } // 是否为接口 if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } // 接口上是否标准了@SPI注解 if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } // ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>() 从缓存中获取 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { // 如果缓存拿不到新创建一个,再加入缓存中 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } private ExtensionLoader(Class<?> type) { this.type = type; // 这里涉及到自适应扩展 先忽略 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); } 复制代码
接着分析这一行:
SPIService loaderOne = extensionLoader.getExtension(“SPIServiceImplOne”);
public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } // 获取默认的Extension if ("true".equals(name)) { return getDefaultExtension(); } // 这里使用了单例模式双重检索 final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { // 创建实例 instance = createExtension(name); holder.set(instance); } } } return (T) instance; } private T createExtension(String name) { // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { // 从缓存中获取 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { // 获取不到创建实例,并加入缓存 //map<class com.lgx.dubbo.spi.impl.SPIServiceImplTwo,SPIServiceImplTwo实例> EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 依赖注入 injectExtension(instance); // 对象包装 Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } } 复制代码
createExtension分为四步:
-
加载配置文件,获取到配置项到配置类的映射关系<name,class>
-
clazz.newInstance()创建实例,加入缓存
-
依赖注入
-
将创建的对象包装到Wrapper实例中
来看看第一步:
// 获取实现类的全限定名称加入到map中形成映射 private Map<String, Class<?>> getExtensionClasses() { // 缓存中获取 Map<String, Class<?>> classes = cachedClasses.get(); // 双重检索 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // 加载接口文件获取实现类的Class类 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); // DUBBO_INTERNAL_DIRECTORY = META-INF/dubbo/internal/ loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); // DUBBO_INTERNAL_DIRECTORY = META-INF/dubbo/ loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); // DUBBO_INTERNAL_DIRECTORY = META-INF/services/ loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; } 复制代码
我们是在META-INF/dubbo/这个目录下创建的 所以会从加载该目录下的文件
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration<java.net.URL> urls; // 获取ClassLoader ==> AppClassLoader ClassLoader classLoader = findClassLoader(); if (classLoader != null) { // 加载文件 urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); // 再这里将每一行的实现类名称和对应的Class类放入到map中形成映射 loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } } // 加载资源 private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { String name = null; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { // 走到这里,加载类并加入缓存 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } } private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } // 自适应扩展先不管 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { // 是否为包装类型 Wrapper cacheWrapperClass(clazz); } else { // 普通扩展类 // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常 clazz.getConstructor(); if (StringUtils.isEmpty(name)) { // 如果name为空,则从Extension注解中取name name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { // 将映射加入缓存中 cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n); } } } } 复制代码
第二步比较简单,来看看第三步:
private T injectExtension(T instance) { try { if (objectFactory != null) { // 遍历该实例的所有方法 for (Method method : instance.getClass().getMethods()) { // 是否为set方法 if (isSetter(method)) { // 是否需要自动注入 方法上是否有DisableInject注解 if (method.getAnnotation(DisableInject.class) != null) { continue; } Class<?> pt = method.getParameterTypes()[0]; // 判断参数是否为原始类型 if (ReflectUtils.isPrimitives(pt)) { continue; } try { // 获取属性名 如: setPerson 则属性名为person String property = getSetterProperty(method); // 调用 SpiExtensionFactory的getExtension方法 Object object = objectFactory.getExtension(pt, property); if (object != null) { // 通过反射执行该方法实现注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; } public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) { // 是否为接口并且接口上标注了SPI注解 if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { // 获取自适应扩展 return loader.getAdaptiveExtension(); } } return null; } } 复制代码
第四步暂时忽略
总结
dubbo的spi机制也比较简单,通过传入的实现类名去缓存找是否有对应的实例,没有就创建。创建的过程就是去META-INF/dubbo/、META-INF/dubbo/internal/、META-INF/services下查找配置文件进行加载,得到<配置名,配置类>的映射关系(其中包括自适应扩展,包装类和普通扩展类),加入到缓存中,根据传入的实现类的名称获取对应的Class类,最后进行实例化获取实现类实例(其中加入了IOC->setter依赖注入,AOP->Wrapper包装实例,并且有大量的缓存)