This article has participated in the weekend learning program, click the link to see more details: juejin.cn/post/696572…

About the SPI

Check out my previous blog posts and follow the link to see more details: juejin.cn/post/696771…

Disadvantages of SPI in the JDK

  1. In the JDK, SPI instantiates all implementations of extension points at once, and loading many implementations of extension points can be time-consuming
  2. Exceptions do not accurately catch hints, and when an implementation of an extension point relies on a third-party library that does not exist, class loading will fail. The error reported is that the extension point could not be found, not that the extension point failed to load, and the real cause

Dubbo’s improvements to SPI

  1. Added default implementation of extension points
  2. Added AOP implementation
  3. Added caching mechanism to improve performance
  4. The configuration file content is changed to the key=value form to address the second disadvantage of SPI mentioned above, in order to match the exception information to the configuration

SPI implementation in Dubbo

  1. The @SPI annotation is used to identify the interface where the Extension point is needed. The @extension annotation was used before, but was later abandoned due to its widespread meaning and replaced with SPI
  2. Dubbo use ExtensionLoader. GetExtensionLoader (Class type) for extension point as an example, the following is the specific implementation
Public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {// If (type == null) {throw new IllegalArgumentException("Extension type == null"); } // Extension point is not an interface, throwing an exception if (! type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!" ); } // Extension point does not use spi annotation if (! withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!" ); } // get the extension point,EXTENSION_LOADERS is a map,key is the extension point interface type,value is an ExtensionLoader object 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; // Extension point type: null if ExtensionLoader, otherwise return extension decorator objectFactory = (type == ExtensionFactory.class? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }Copy the code
  1. Returns an extension point object with the specified name
public T getExtension(String name) { if (name == null || name.length() == 0) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } Holder<Object> Holder = cachedInstances. Get (name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<Object>()); holder = cachedInstances.get(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; }Copy the code
  1. CachedInstances implementation
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); Public class Holder<T> {/** * value thread visibility */ private volatile T value; public void set(T value) { this.value = value; } public T get() { return value; }}Copy the code

SPI is used specifically in Dubbo

  1. Take Protocol as an example. By default, the Dubbo protocol is used. Dubbo supports the default SPI extension point
@SPI("dubbo") public interface Protocol { //..... Omit code}Copy the code
  1. SPI configuration file
/ / com. Alibaba. Dubbo, RPC Protocol file inside the configuration of the dubbo = com. Alibaba. Dubbo.. RPC Protocol. The dubbo. DubboProtocolCopy the code
  1. DubboProtocol
*/ public static DubboProtocol getDubboProtocol() {if */ public static DubboProtocol getDubboProtocol() {if (INSTANCE == null) { ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load } return INSTANCE; }Copy the code