概述
首先了解一下JDK的SPI
- spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。
- 这个是针对厂商或者插件的。
- 一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。
实例
package com.tan.spi.example;
public interface People {
void eat(String food);
}
package com.tan.spi.example;
public class Tom implements People{
@Override
public void eat(String food) {
System.out.print("Tom 在吃"+food);
}
}
package 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进去
private static final ExtensionLoader<Container> loader =
ExtensionLoader.getExtensionLoader(Container.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 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注解
/**
* 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
@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()方法
public 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方法,
private 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);
}
}
private 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扩展所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复