以前写了一篇关于dubbo spi的文章,如今看来后面的代码交代的不清楚,今天作一点补充。java
http://my.oschina.net/zjItLife/blog/530923编程
dubbo的spi重要的是理解类ExtensionLoader。缓存
这个类的头部做者就交代了这个类要作什么app
Dubbo使用的扩展点获取。this
自动注入关联扩展点。.net
自动Wrap上扩展点的Wrap类。code
缺省得到的的扩展点是一个Adaptive Instance。blog
那么到底是怎么作的咱们不妨来看看他的变量在作什么。接口
dubbo变量里面有两个表示文件夹的变量get
private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
下文涉及到这三个变量的方法在这里。
LoadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); LoadFile(extensionClasses, SERVICES_DIRECTORY);
也就是说扩展类extensionClasses都是从这三个文件夹里面获取的。 接下来的两个变量
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
下文的方法都是从这两个容器得到ExtensionLoader类和ExtensionLoader类实例
接下来是两个变量
private final Class<?> type; private final ExtensionFactory objectFactory;
一个是该ExtensionLoader类表明的接口类型还有就是生成ExtensionLoader的适配器工厂。
接下来是缓存容器
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>(); private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); private volatile Class<?> cachedAdaptiveClass = null; private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); private String cachedDefaultName; private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); private volatile Throwable createAdaptiveInstanceError; private Set<Class<?>> cachedWrapperClasses;
还有一个容错容器
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
接下来咱们看看各个方法都作了什么 一、何时生成了这个ExtensionLoader
dubbo代码中不少地方都调用了ExtensionLoader类的方法getExtensionLoader。该方法采用单利模式生成ExtensionLoader类的实例。
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!"); } //判断是否打了注解@SPI if(!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } //先从容器中获取 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) { //生成的时候将类型传入,并生成一个类型为ExtensionFactory的适配器,赋值给objectFactory。 this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
二、基于类型的适配器类和扩展类是如何被扫描进来的?
在交代变量的时候咱们发现了扫描上文件夹下的类集合,那么这些获取扩展类的方法在方法loadExtensionClasses中
// 此方法已经getExtensionClasses方法同步过。 private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { //获取@SPI里面的值,这个值后来用来获取默认的实现类。 String value = defaultAnnotation.value(); if(value != null && (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]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
这个类的做用只是将三个文件夹里面与type一致的名称和类的映射关系维护到类容器中,咱们仔细看看这个类。
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { //获取文件路径,由于dubbo的各个配置文件符合文件夹+接口名称这样的规则 //1获取类加载器 //二、根据类加载器加载文件 //三、遍历文件 //四、将每一行信息按照“=”分隔。 //4.一、获取=右面的类。 //4.二、保证获取的类是不是type接口的实现类。 //4.三、若是类标有@Adaptive,则将这个类放置到适配器类的位置。(适配器类只能是一个) //4.四、若是不是适配器类,则将其其名称和类之间的关系维护到容器cachedNames和extensionClasses中 }
三、dubbo spi重要的就是在初期生成了一个适配器类,用作以后找到真正实现类的中转站。
injectExtension((T) getAdaptiveExtensionClass().newInstance());
一、生成适配器类
二、实例化适配器类
三、若是是工厂类的适配器则injectExtension未作任何事情。若是非工厂类会获取全部set方法的属性值,若是工厂类可以查找到相应的适配器类,则将其注入进来。
四、 寻找扩展点,dubbo spi费了半天的劲儿去实例化了一个适配器,做用仍是在生成扩展点起做用。
4.一、获取缺省的扩展点
上文已经交代,在生成ExtensionLoader实例的时候就找到了一个默认的扩展点名称,将其放置到了变量cachedDefaultName中。根据这个名称,咱们能够生成默认的扩展点实例。
public T getDefaultExtension() { getExtensionClasses(); if(null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); }
4.二、获取指定名称的扩展点
T getExtension(String name)
4.三、获取指定名称的扩展类
private Class<?> getExtensionClass(String name)
五、编程方式注入扩展点
5.一、编程方式添加新扩展点
public void addExtension(String name, Class<?> clazz)
5.二、编程方式添加替换已有扩展点
public void replaceExtension(String name, Class<?> clazz)
至此,ExtensionLoader类就交代完了,对于这个类的理解将对咱们深刻理解dubbo源码有必定帮助。
做者hyssop,请君留言批评指正。