为什么80%的码农都做不了架构师?>>>   

dubbo框架的微内核+插件的机制,其中插件机制就是依赖ExtensionLoader来实现,可以说是dubbo的核心所在。通过插件机制解耦依赖来实现框架设计原则中的针对接口编程而不针对实现。 
熟悉ExtensionLoader就熟悉了Dubbo的扩展机制。

JDK的标准SPI对比dubbo的SPI

JDK的扩展SPI:

新建接口,然后定义不同实现,然后/META-INF/services定义接口的全路径的文件,文件中写上接口的全部实现类,最后代码通过ServiceLoader加载,循环迭代得到所有实现类。标准SPI迭代时会加载所有实现,所以只希望加载某个的,就不现实了。 
Dubbo的扩展SPI:

1. 单例,对于某个扩展,只会有一个ExtensionLoader; 
    2. 延迟加载,可以一次只获取想要的扩展点,一次获取想要的扩展点实现; 
    3. 对于扩展点的Ioc和Aop,就是一个扩展可以注入到另一个扩展中,也可以对一个扩展做wrap包装实现aop的功能; 
    4. 对于扩展点的调用,真正调用的时候才能确认具体使用的是那个实现。

源码解析

所有扩展必须加上SPI注解才能通过ExtensionLoader加载。对于一个扩展点只会加载一次,生成一个ExtensionLoader。通过ExtensionLoader.getExtensionLoader(class type)传入一个spi扩展返回一个ExtensionLoader实例。

    private ExtensionLoader(Class<?> type) {this.type = type;// 也是利用扩展实现,后面会看到在注入其他扩展点或bean到当前扩展时使用objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());} 
    @SuppressWarnings("unchecked")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) {// 创建一个ExtensionLoader返回EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);}return loader;}

Dubbo中的扩展点均有多个实现,而框架设计原则又让我们针对接口编程而不是实现,这就需要在运行期才能决定具体使用哪个扩展实现类。Dubbo提供了Adpative注解,让我们自行决定究竟是自己提供扩展的适配还是由Dubbo来帮我们生成动态适配。后面会给出2个自定义扩展实现Demo,一个是我们自定义适配,另一个是Dubbo提供动态适配。

常用方式:ExtensionLoader.getExtensionLoader(扩展点class).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;}
    /** 创建Adpative */private T createAdaptiveExtension() {try {return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);}}

这里分两步:

1. getAdaptiveExtensionClass:获取适配器类; 
2. injectExtension:为适配器类的setter方法插入其他扩展点或实现bean。

    /** 获取适配器类 */private Class<?> getAdaptiveExtensionClass() {// 加载所有扩展点实现类getExtensionClasses();// 如果用户有自定义适配器实现类,returnif (cachedAdaptiveClass != null) {return cachedAdaptiveClass;}// dubbo帮我们动态创建一个适配器类return cachedAdaptiveClass = createAdaptiveExtensionClass();}/** 加载扩展类 */private Map<String, Class<?>> getExtensionClasses() {Map<String, Class<?>> classes = cachedClasses.get();if (classes == null) {synchronized (cachedClasses) {classes = cachedClasses.get();if (classes == null) {// 加载扩展类classes = loadExtensionClasses();cachedClasses.set(classes);}}}return classes;}/** 从不同目录加载扩展实现 */private Map<String, Class<?>> loadExtensionClasses() {// Dubbo的扩展接口必须实现SPI注解final SPI defaultAnnotation = type.getAnnotation(SPI.class);// 这里是解析SPI注解,看是否有默认实现,后期Dubbo获取扩展的实现,如果获取不到就会有使用这个默认实现if (defaultAnnotation != null) {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];}}//从3个目录加载Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);//META-INF/dubbo/internal/loadFile(extensionClasses, DUBBO_DIRECTORY);//META-INF/dubbo/loadFile(extensionClasses, SERVICES_DIRECTORY);//META-INF/services/return extensionClasses;}
    /** 从目录加载扩展实现 */private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {String fileName = dir + type.getName();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 url = urls.nextElement();try {BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));try {String line = null;while ((line = reader.readLine()) != null) {final int ci = line.indexOf('#');if (ci >= 0) line = line.substring(0, ci);line = line.trim();if (line.length() > 0) {try {String name = null;int i = line.indexOf('=');if (i > 0) {// 文件采用properties方式的配置,name=实现类的全路径name = line.substring(0, i).trim();line = line.substring(i + 1).trim();}if (line.length() > 0) {Class<?> clazz = Class.forName(line, true, classLoader);               // 必须是该扩展点的实现if (!type.isAssignableFrom(clazz)) {throw new IllegalStateException("Error when load extension class(interface: " +type + ", class line: " + clazz.getName() + "), class "+ clazz.getName() + "is not subtype of interface.");}// 这里判断是否有自定义的适配器类,如果有,后面获取适配器的时候,就可以直接用这个创建返回,不用dubbo动态创建if (clazz.isAnnotationPresent(Adaptive.class)) {if (cachedAdaptiveClass == null) {cachedAdaptiveClass = clazz;} else if (!cachedAdaptiveClass.equals(clazz)) {throw new IllegalStateException("More than 1 adaptive class found: "+ cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName());}} else {try {// 这里处理的是,实现类的包裹类,必须是构造器注入扩展点,这里后期获取具体扩展点的实现类时,会使用包裹类封装下clazz.getConstructor(type);Set<Class<?>> wrappers = cachedWrapperClasses;if (wrappers == null) {cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();wrappers = cachedWrapperClasses;}wrappers.add(clazz);} catch (NoSuchMethodException e) { // 处理不是包裹类的情况clazz.getConstructor();if (name == null || name.length() == 0) {name = findAnnotationName(clazz);if (name == null || name.length() == 0) {if (clazz.getSimpleName().length() > type.getSimpleName().length()&& clazz.getSimpleName().endsWith(type.getSimpleName())) {name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();} else {throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);}}}String[] names = NAME_SEPARATOR.split(name);if (names != null && names.length > 0) {// 判断下Activate注解,后面讲,这里只需要知道,会缓存Activate注解的实现Activate activate = clazz.getAnnotation(Activate.class);if (activate != null) {cachedActivates.put(names[0], activate);}for (String n : names) {if (!cachedNames.containsKey(clazz)) {cachedNames.put(clazz, n);}Class<?> c = extensionClasses.get(n);if (c == null) {// 缓存扩展实现extensionClasses.put(n, clazz);} else if (c != clazz) {throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());}}}}}}} catch (Throwable t) {IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);exceptions.put(line, e);}}} // end of while read lines} finally {reader.close();}} catch (Throwable t) {logger.error("Exception when load extension class(interface: " +type + ", class file: " + url + ") in " + url, t);}} // end of while urls}} catch (Throwable t) {logger.error("Exception when load extension class(interface: " +type + ", description file: " + fileName + ").", t);}}
    /** Dubbo生成适配类 */private Class<?> createAdaptiveExtensionClass() {// 动态生成适配器代码String code = createAdaptiveExtensionClassCode();ClassLoader classLoader = findClassLoader();// 动态编译生成适配器类com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();return compiler.compile(code, classLoader);}

createAdaptiveExtensionClassCode方法生成扩展接口的适配: 
1. 解析Adpative注解,从url或扩展接口获取扩展接口实现类的名称; 
2. 根据名称,获取实现类ExtensionLoader.getExtensionLoader(扩展接口类).getExtension(扩展接口实现类名称),然后调用实现类的方法。

createAdaptiveExtensionClassCode生成适配器代码,可以自己debug看看。主要是处理Adpative注解在方法上的方式,(需要明白一点dubbo的内部传参基本上都是基于Url来实现的,例如注册服务的地址,订阅服务的地址啊什么的都是一串url):

适配器类的目的是在运行期获取扩展的真正实现来调用,解耦接口和实现,这样的话要不我们自己实现适配器类,要不dubbo帮我们生成,而这些都是通过Adpative来实现。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {/*** 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。* <p>* 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>* 比如,<code>String[] {"key1", "key2"}</code>,表示* <ol>* <li>先在URL上找key1的Value作为要Adapt成的Extension名;* <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。* <li>key2没有Value,使用缺省的扩展。* <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。* </ol>* <p>* 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>* 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为<code>String[] {"yyy.invoker.wrapper"}</code>** @see SPI#value()*/String[] value() default {};}

这里给出2个适配生成的代码,体会下。

  • protocol协议
@SPI("dubbo")
public interface Protocol {int getDefaultPort();@Adaptive<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;@Adaptive<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;void destroy();
}

生成的代码:

package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {if (arg1 == null) {throw new IllegalArgumentException("url == null");}com.alibaba.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null) {throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString()+ ") use keys([protocol])");}com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0)throws com.alibaba.dubbo.rpc.Invoker {if (arg0 == null) {throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");}if (arg0.getUrl() == null) {throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");}com.alibaba.dubbo.common.URL url = arg0.getUrl();String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null) {throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString()+ ") use keys([protocol])");}com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}
}
  • Validation
@SPI("jvalidation")
public interface Validation {@Adaptive(Constants.VALIDATION_KEY)Validator getValidator(URL url);}

生成的代码:

package com.alibaba.dubbo.validation;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Validation$Adpative implements com.alibaba.dubbo.validation.Validation {public com.alibaba.dubbo.validation.Validator getValidator(com.alibaba.dubbo.common.URL arg0) {if (arg0 == null) {throw new IllegalArgumentException("url == null");}com.alibaba.dubbo.common.URL url = arg0;String extName = url.getParameter("validation", "jvalidation");if (extName == null) {throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.validation.Validation) name from url(" + url.toString() + ") use keys([validation])");}com.alibaba.dubbo.validation.Validation extension = (com.alibaba.dubbo.validation.Validation) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.validation.Validation.class).getExtension(extName);return extension.getValidator(arg0);}
}

injectExtension

    /** 插入该扩展需要其他扩展或bean */private T injectExtension(T instance) {try {// objectFactory也是一个扩展点,在new ExtensionLoader的时候创建if (objectFactory != null) {for (Method method : instance.getClass().getMethods()) {// 必须是set方法注入if (method.getName().startsWith("set")&& method.getParameterTypes().length == 1&& Modifier.isPublic(method.getModifiers())) {Class<?> pt = method.getParameterTypes()[0];try {String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";// objectFactory获取扩展点或bean注入到当前扩展点Object object = objectFactory.getExtension(pt, property);if (object != null) {method.invoke(instance, object);}} catch (Exception e) {logger.error("fail to inject via method " + method.getName()+ " of interface " + type.getName() + ": " + e.getMessage(), e);}}}}} catch (Exception e) {logger.error(e.getMessage(), e);}return instance;}

这里的objectFactory.getExtension(pt, property),objectFactory(ExtensionFactory)在new ExtensionLoader时会创建

    private ExtensionLoader(Class<?> type) {this.type = type;objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());}

看下ExtensionFactory这个扩展点。

注解Adpative加载AdaptiveExtensionFactory类上表示这是个自定义的适配器类,有2个实现类,一个是Spi(从dubbo 的扩展中获取),一个spring(获取spring的bean),AdaptiveExtensionFactory轮询这2个,从一个中获取到就返回。

getActivateExtension

getActivateExtension方法基本都会传入url作为参数,用法是获取激活条件的所有扩展点实现类。例如:获取当前消费端所有的filter。

这里用到了另一个注解Activate:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {/*** Group过滤条件。* <br />* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。* <br />* 如没有Group设置,则不过滤。*/String[] group() default {};/*** Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。* <p/>* 示例:<br/>* 注解的值 <code>@Activate("cache,validatioin")</code>,* 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。* <br/>* 如没有设置,则不过滤。*/String[] value() default {};/*** 排序信息,可以不提供。*/String[] before() default {};/*** 排序信息,可以不提供。*/String[] after() default {};/*** 排序信息,可以不提供。*/int order() default 0;
}

group基本表示用在服务端还是消费端,value表示激活这个扩展点的条件,before、after、order用于排序。

    /**获取满足激活条件的扩展实现*/public List<T> getActivateExtension(URL url, String[] values, String group) {List<T> exts = new ArrayList<T>();List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {// 加载扩展getExtensionClasses();// 上一步会缓存所有Active注解的实现类到cachedActivatesfor (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {String name = entry.getKey();Activate activate = entry.getValue();// 匹配group,provider还是consumerif (isMatchGroup(group, activate.group())) {// 获取扩展实现类T ext = getExtension(name);// isActive匹配激活条件if (!names.contains(name)&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)&& isActive(activate, url)) {exts.add(ext);}}}// 这里会重新排序,用到Active里面before、after和orderCollections.sort(exts, ActivateComparator.COMPARATOR);}List<T> usrs = new ArrayList<T>();for (int i = 0; i < names.size(); i++) {String name = names.get(i);if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {if (Constants.DEFAULT_KEY.equals(name)) {if (usrs.size() > 0) {exts.addAll(0, usrs);usrs.clear();}} else {T ext = getExtension(name);usrs.add(ext);}}}if (usrs.size() > 0) {exts.addAll(usrs);}return exts;}

isMatchGroup:

/** group匹配,provider还是consumer端 */
private boolean isMatchGroup(String group, String[] groups) {// group没有,那就是不管Active是provider还是consumer端的都可以if (group == null || group.length() == 0) {return true;}// Active的注解group值,必须配置,不配置,那就都不能使用,配置后匹配if (groups != null && groups.length > 0) {for (String g : groups) {if (group.equals(g)) {return true;}}}return false;
}

isActive:

    /** Activate激活条件匹配 */private boolean isActive(Activate activate, URL url) {String[] keys = activate.value();// 如果Active注解的value为null,那就表示不需要条件都可以使用if (keys == null || keys.length == 0) {return true;}// value不为null,那就跟url里面的参数匹配下for (String key : keys) {for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {String k = entry.getKey();String v = entry.getValue();if ((k.equals(key) || k.endsWith("." + key))&& ConfigUtils.isNotEmpty(v)) {return true;}}}return false;}

getExtension

生成的适配器类和getActiveExtension里面都有getExtension方法,用于获取扩展的实现类。

/** 根据名称获取扩展 */
public T getExtension(String name) {if (name == null || name.length() == 0)throw new IllegalArgumentException("Extension name == null");// 如果为名称为true,则获取默认    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;
}

createExtension

/** 创建扩展点实例 */
private T createExtension(String name) {Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(name);}try {T instance = (T) EXTENSION_INSTANCES.get(clazz);if (instance == null) {EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());instance = (T) EXTENSION_INSTANCES.get(clazz);}//注入其他扩展点或beaninjectExtension(instance);//包裹类封装Set<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && wrapperClasses.size() > 0) {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 + ")  could not be instantiated: " + t.getMessage(), t);}
}

2种自定义扩展实现

在上一个dmeo的新建spi module。

Dubbo提供Adpative动态适配

扩展点1,DirExtension.java

@SPI("dirDefault")
public interface DirExtension {@Adaptive({SpiConstants.DIR_DEFAULT, SpiConstants.DIR_FIRST})String sayHello(URL url, String name);
}

2个扩展点实现 
DirExtensionDefaultImpl.java

public class DirExtensionDefaultImpl implements DirExtension {@Overridepublic String sayHello(URL url, String name) {return name + "-defualt";}
}
  • DirExtensionFirstImpl.java
public class DirExtensionFirstImpl implements DirExtension {@Overridepublic String sayHello(URL url, String name) {return name + "-first";}
}
  • 常量类SpiConstants.java
public class SpiConstants {public static final String DIR_DEFAULT = "dirDefault";public static final String DIR_FIRST = "dirFirst";public static final String DIR2_DEFAULT = "dir2Default";public static final String DIR2_FIRST = "dir2First";}
  • 配置的扩展点com.qbb.spi.DirExtension
dirDefault=com.qbb.spi.DirExtensionDefaultImpl
dirFirst=com.qbb.spi.DirExtensionFirstImpl
  • 测试类DirExtensionTest.java:
public class DirExtensionTest {public static void main(String[] args){ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(DirExtension.class);//DirExtension dirExtension = (DirExtension)extensionLoader.getExtension("dirFirst");DirExtension dirExtension = (DirExtension)extensionLoader.getAdaptiveExtension();Map<String, String> parameters = new HashMap<String, String>();//parameters.put("dirFirst", "dirFirst");parameters.put("dirDefault", "dirDefault");URL url = new URL("dubbo", "127.0.0.1", 8089, parameters);System.out.println(dirExtension.sayHello(url, "qbb"));}
}
  • 自定义Adpative适配

扩展点2,Dir2Extension.java

@SPI("dir2Default")
public interface Dir2Extension {String sayHello(String name, String type);
}
  • 2个扩展点2的实现 
    Dir2ExtensionDefaultImpl.java
public class Dir2ExtensionDefaultImpl implements Dir2Extension {@Overridepublic String sayHello(String name, String type) {return "dir2Extension-" + name + "-default";}
}
  • Dir2ExtensionFirstImpl.java
public class Dir2ExtensionFirstImpl implements Dir2Extension {@Overridepublic String sayHello(String name, String type) {return "dir2Extension-" + name + "-first";}
}
  • 自定义Adpative 
    Dir2ExtensionAdaptive.java
@Adaptive
public class Dir2ExtensionAdaptive implements Dir2Extension{@Overridepublic String sayHello(String name, String type) {ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Dir2Extension.class);Dir2Extension dir2Extension = (Dir2Extension)extensionLoader.getDefaultExtension();if (type != null && SpiConstants.DIR2_FIRST.equalsIgnoreCase(type.trim())) {dir2Extension = (Dir2ExtensionDefaultImpl)extensionLoader.getExtension(SpiConstants.DIR2_FIRST);}return dir2Extension.sayHello(name, type);}}
  • 扩展点2的配置
adaptive=com.qbb.spi2.Dir2ExtensionAdaptive
dir2Default=com.qbb.spi2.Dir2ExtensionDefaultImpl
dir2First=com.qbb.spi2.Dir2ExtensionFirstImpl
  • 测试类 
    Dir2ExtensionTest.java
public class Dir2ExtensionTest {public static void main(String[] args){ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Dir2Extension.class);Dir2Extension dirExtension = (Dir2Extension)extensionLoader.getAdaptiveExtension();String type = null;//type = SpiConstants.DIR2_DEFAULT;//type = SpiConstants.DIR2_FIRST;System.out.println(dirExtension.sayHello("qbb2", type));}
}

转载于:https://my.oschina.net/jzgycq/blog/1589772

Dubbo源码分析(三):ExtensionLoader相关推荐

  1. Dubbo源码分析(三) -- Dubbo的服务发现源码深入解析4万字长文

    前言 前面两篇基本上已经对dubbo的SPI,服务发布,注册等功能进行了分析,那么在消费端是如何发现服务,并进行透明的远程调用的呢?带着这个疑问,走入今天的篇章,Dubbo的服务发现 服务发现的流程 ...

  2. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  3. Dubbo 源码分析 - 服务导出

    1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可 ...

  4. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

  5. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  6. Dubbo 源码分析 - 集群容错之 Router

    1. 简介 上一篇文章分析了集群容错的第一部分 – 服务目录 Directory.服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由.上一篇文章关于服务路由相关逻辑没有细 ...

  7. 精尽 Dubbo 源码分析 —— API 配置

    1. 概述 Dubbo 的配置目前提供了四种配置方式:1. API 配置 2. 属性配置 3. XML 配置 4. 注解配置 2. 配置一览 我们来看看 dubbo-config-api 的项目结构, ...

  8. Dubbo源码分析:小白入门篇

    关注公众号"java后端技术全栈" 回复"000"获取优质面试资料 大家好,我是老田 答应了小伙伴的Dubbo源码分析系列,今天终于来了,希望不是很晚. 主要也 ...

  9. 志宇-dubbo源码分析

    dubbo源码分析 文档 dubbo加载配置文件 dubboSPI dubbo服务提供 1.校验配置信息 2.创建URL 3.本地注册 4.远程注册 4.1 开启netty服务端 4.2 连接注册中心 ...

  10. Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块 ...

最新文章

  1. PingCode Wiki 多人实时协同编辑功能发布
  2. P值(P-value),“差异具有显著性”和“具有显著差异”
  3. Oracle Database 12c(12.1) Beta已经开始内部测试
  4. 查看命令为内置命令还是外部命令
  5. margin塌陷问题
  6. 【转载】【面试题】你是一个测试工程师,如何保证软件质量?
  7. 米范 - 为新媒体和电商从业者提供的酷导航
  8. PADS 默认过孔太大,过孔提前设置
  9. 【二 HTTP编程】2. HTTP路由
  10. 由中缀表达式计算前缀表达式
  11. 新浪微博Emoji表情解析
  12. wps怎样删除空白页 WPS文档的空白页如何删除
  13. 庞加莱猜想的证明过程
  14. 关于“上家公司离职原因”应聘者回答技巧分享
  15. 异构蜂窝网络K-Tier下行链路的建模与matlab分析
  16. Java 比较字符串之间大小
  17. LeetCode224基本计算器用DFA实现超简洁
  18. 丹尼斯·里奇-C语言创始人
  19. JavaScript的前生今世
  20. 北京公交一卡通可打公用电话 节省60%话费

热门文章

  1. QTP提示加载数据表文件时出错的解决方案
  2. 引用 一个较优雅的GridView隐藏列取值解决方案
  3. Django、Flask、Tornado的区别
  4. 剑指offer-从尾到头打印链表03
  5. 基于PSR-0编码规范开发一套PHP-MVC框架(二)
  6. MySQL的安装和基本操作
  7. python编写图片主色转换脚本
  8. js中substr,substring,indexOf,lastIndexOf,split 的用法
  9. window下Ionic环境安装
  10. Java基础:Util包下常用的数据结构介绍