一、SPI
-
java中的SPI(service provider interface),数据库驱动 java.sql.Driver, 通过加载在MATA-INF/services下以java.sql.Driver,命名的文件
数据库驱动厂商自己实现,比如mysql
-
dubbo很好的借鉴了这种SPI机制,dubbo会加载META-INF/services,META-INF/dubbo和META-INF/dubbo/internal三个目录下的文件,我们定义一个MyProtocol的协议和org.apache.dubbo.rpc.Protocol的文件
文件内容为:myprotocol=com.gp.patterns.MyProtocol
利用dubbo提供的api测试
执行后,端口为9527,我们看下ExtensionLoader都做了什么。 -
ExtensionLoader分析
#1. 获取ExtensionLoader public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //EXTENSION_LOADERS缓存对应type的ExtensionLoader 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; }
#2.获取对应name的实例 public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } //使用默认配置 if ("true".equals(name)) { return getDefaultExtension(); } //用于保存name对应的实例 Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { #3. 创建这个实例 instance = createExtension(name); holder.set(instance); } } } return (T) instance; }
#3. 创建这个实例 private T createExtension(String name) { #4.获取扩展点class Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { //EXTENSION_INSTANCES缓存这个扩展点 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //dubbo的set方法注入实例,用到了dubbo SPI的自适应扩展点,要求set方法的参数必须是一个扩展点 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); } }
#4.获取扩展点class private Map<String, Class<?>> getExtensionClasses() { //cachedClasses是一个Holder类 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { #5.加载扩展点class 并缓存 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
#5.加载扩展点class 并缓存 private Map<String, Class<?>> loadExtensionClasses() { #6.缓存默认的扩展点 @SPI注解的默认值 cacheDefaultExtensionName(); //将三个目录下的文件里面对应的key value 加载进extensionClasses 中 Map<String, Class<?>> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }
#6.缓存默认的扩展点 @SPI注解的默认值 private void cacheDefaultExtensionName() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) { cachedDefaultName = names[0]; } } } }
二、dubbo的扩展点
-
dubbo的静态扩展点
1.静态扩展点 public class ExtensionTest { public static void main(String[] args) { //通过名字获取的就是静态扩展点 Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol"); System.out.println(protocol.getDefaultPort()); } }
-
dubbo的自适应扩展点
1. 自适应扩展点 public class ExtensionTest { public static void main(String[] args) { //自适应扩展点,返回AdaptiveCompiler, Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension(); compiler.compile("", null); System.out.println(compiler); } }
1. 根据name自适应用什么compiler, dubbo提供两种 jdk和javassist,默认是@SPI("javassist"), 2. @Adaptive在类上表示是一个自适应扩展类 @Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } }
1.@Adaptive如果在方法上,则动态代理生成对应的扩展点 2.ExetensionLoader的loadClass方法会判断是不是标注在类上, public T getAdaptiveExtension() { .... instance = createAdaptiveExtension(); .... return (T) instance; } private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); //loadClass会判断@Adaptive标注在哪里 //类上 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } //方法上 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
-
dubbo的激活扩展点
1.类似Springboot的Conditional, public class ExtensionTest { public static void main(String[] args) { //激活扩展点 ExtensionLoader<Filter> extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class); URL url = new URL("", "", 0); //url驱动,添加参数就像 dubbo的:@Service(cache = FilterConstants.CACHE_KEY) //加了cache,就加载CacheFilter url = url.addParameter("cache", "cache"); List<Filter> filters = extensionLoader.getActivateExtension(url, "cache"); System.out.println(filters.size()); } }
1.group表示能用在服务端还是客户端 @Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY) public class CacheFilter implements Filter { }