首先了解一下JDK的SPI
- spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。
- 这个是针对厂商或者插件的。
- 一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。
实例
1
2
3
4
5
6
7
8package com.tan.spi.example; public interface People { void eat(String food); }
1
2
3
4
5
6
7
8
9
10
11
12
13package com.tan.spi.example; public class Tom implements People{ @Override public void eat(String food) { System.out.print("Tom 在吃"+food); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.tan.spi.example; import java.util.Iterator; import java.util.ServiceLoader; public class Main { public static void main(String[] args) { System.out.print("llll"); ServiceLoader<People> load = ServiceLoader.load(People.class); Iterator<People> iterator = load.iterator(); while (iterator.hasNext()) { People next = iterator.next(); next.eat("蛋糕"); } } }
src下面必须新建META-INF/services/接口全限定路径
结果截图
这里我们只是了解一下,具体还可以自行百度
Dubbo 扩展SPI机制
Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了
ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。
上面我们已经实验了java的spi,现在我们来看一下dubbo自定义实现的spi,这里有一个问题,dubbo为什么不用jdk的spi机制 而要自己定义一个???
- jdk标准的spi会一次性实例化扩展点上面的所有实现,如果所有扩展实现会很耗时,没有用到的也进行加载会造成资源浪费
- 增加都扩展点ioc和aop的支持,一个扩展点可以直接setter注入其他扩展点
约定
dubbo spi 存储路径 META-INF/dubbo/internal 文件名为接口全路径名,每一个spi定义的格式为扩展名=具体类目
目地
获取一个实现类的对象
途径
-
getExtensionLoader(Class type) 为该接口new 一个ExtensionLoader,然后缓存起来
-
getAdaptiveExtension()
获取一个扩展装饰类对象,这个类有一个规则,如果没有一个adaptive注解,就会动态创建一个装饰类 -
getExtension(String name)获取一个实现类的对象
具体实现
首先看一下getExtensionLoader的源码,从 org.apache.dubbo.container.Container进去
1
2
3
4private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public 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 interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 这里是从一个ConcurrentMap中取,如果没有会将new 一个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; }
在看一下getAdaptiveExtension
这是adaptive注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/** * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance. * * @see ExtensionLoader * @see URL */ // ElementType.TYPE 代表该注解只能注解在类,包上面 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { String[] value() default {}; }
需要注意的是,adaptive注解和方法上是存在一定区别的:
注解在类上:代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),比如ExtensionFactory
注解在方法上,代表动态的生成和编译一个动态的adaptive,例如Protoco$Adaptive
首先从**org.apache.dubbo.config.spring.schema.DubboNamespaceHandler(dubbo命名空间)**进去,看一下seviceBean.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14@Override public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }
serviceBean实现了ServiceConfig,我们看一下这个方法,
发现里面的getAdaptiveExtension()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
主要看一下createAdaptiveExtension方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration<java.net.URL> urls; 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(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { // 拼装路径 String fileName = dir + type; try { Enumeration<java.net.URL> urls; 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(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
最后
以上就是轻松秀发最近收集整理的关于Dubbo与spi扩展的全部内容,更多相关Dubbo与spi扩展内容请搜索靠谱客的其他文章。
发表评论 取消回复