前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

SPI 全称为 Service Provider Interface,是一种服务发现机制。

SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类

正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。

Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。接下来,我们先来了解一下 Java SPIDubbo SPI 的用法,然后再来分析 Dubbo SPI 的源码。

1、SPI 实例

1.1、Java SPI

1.1.1、接口Robot

/**
 * 机器人
 */
public interface Robot {

    void sayHello();
}

1.1.1.1、实现类OptimusPrime

/**
 * 擎天柱
 */
@Slf4j
public class OptimusPrime implements Robot {

    @Override
    public void sayHello() {
        log.info("Hello, I am Optimus Prime.");
    }
}

1.1.1.2、实现类Bumblebee

/**
 * 大黄蜂
 */
@Slf4j
public class Bumblebee implements Robot {

    @Override
    public void sayHello() {
        log.info("Hello, I am Bumblebee.");
    }
}

1.1.2、 接口全限名配置文件

resources -> META-INFO ->services

com.healerjean.proj.study.spi.OptimusPrime
com.healerjean.proj.study.spi.Bumblebee

image-20201102151734868

1.1.3、Main方法测试

/**
 * java spi 测试
 */
@Slf4j
public class JavaSPITest {

    @Test
    public void sayHello() {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        log.info("Java SPI--------");
        serviceLoader.forEach(robot -> robot.sayHello());
    }
}

1.1.3.1、控制台日志

Java SPI--------
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

1.2、Dubbo SPI

继续使用上面的代码,对其进行改动一点点

1.2.1、接口添加注解@SPI

@SPI
public interface Robot {

    void sayHello();

}

1.2.2、配置文件

dubbo配置文件一般在dubbo文件夹中,但是其实通过后面的源码我们也可以知道,不一定非要在这个文件夹中,其他文件夹也是可以的 ,

optimusPrime=com.healerjean.proj.study.spi.d01_java.api.impl.OptimusPrime
bumblebee=com.healerjean.proj.study.spi.d01_java.api.impl.Bumblebee

image-20201102152958554

1.2.3、Main方法启测试

@Slf4j
public class DubboSPITest {

    @Test
    public void sayHello()  {
        ExtensionLoader<Robot> extensionLoader =  ExtensionLoader.getExtensionLoader(Robot.class);
        log.info("获取接口实现类的名称 {}", extensionLoader.getSupportedExtensions());
        log.info("----------------------------");

        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();

        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

1.2.3.1、控制台日志

获取接口实现类的名称 ["bumblebee","optimusPrime"]
----------------------------
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

1.3、@Adaptive 注解使用

在类,以及方法上调用。定义了 注解的方法,参数列表中一定要有类型为 URL 的参数。

1、在类上加上 @Adaptive 注解的类,是最为明确的创建对应类型Adaptive类。所以他优先级最高

2、如果作用在方法上,注解中的 value 与链接中的参数的 key 一致,链接中的 key 对应的 value 就是 spi 中的name,获取相应的实现类。,如果未设置 value,则根据接口名生成 value,比如接口 Animal 生成 url 地址参数名= “animal”。

3、如果没有在某个实现类加 @Adaptive,则默认的扩展又 @SPI注解指定,此时如果URL中可以获取到真实的值(如果是错误的则会报错),就用URL中的,如果通过URL获取不到关于取哪个类作为Adaptive类的话,就使用这个@SPI注解指定的默认值,如果这个默认值也找不到则包异常

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
 
    String[] value() default {};

}

1.3.1、实例说明

1.3.1.1、实例1

@SPI注解中有value值

1.3.1.1.1、示例代码

接口

@SPI(value = "adaptiveAnimal") //@SPI指定默认的扩展类
public interface Animal {

    @Adaptive
    void call(String msg, URL url);

}

扩展类

@Slf4j
public class AdaptiveAnimal implements Animal {

    @Override
    public void call(String msg, URL url) {
      log.info("我是适配:{}", msg);
    }
}

@Slf4j
public class Cat implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是猫: {}", msg);
    }
}



@Slf4j
public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗: {},", msg);
    }
}


1.3.1.1.2、启动测试
/**
 * 1、 @SPI注解中有value值,URL中没有具体的值
 * 2 、@SPI注解中有value值,URL中有具体的值
 */
@Test
public void testAdaptive1()  {
    ExtensionLoader<Animal> annoimalExtensionLoader =  ExtensionLoader.getExtensionLoader(Animal.class);
    Animal adaptiveExtension = annoimalExtensionLoader.getAdaptiveExtension();
    
    //1、 @SPI(value = "adaptiveAnimal"),URL中没有具体的值,则获取的是适配类
    URL url = URL.valueOf("test://localhost/test");
    adaptiveExtension.call("哒哒哒", url);
    // 我是适配:哒哒哒
    
    //2、@SPI(value = "adaptiveAnimal"),URL中有具体的值
    url = URL.valueOf("test://localhost/test?animal=cat");
    adaptiveExtension.call("喵喵喵", url);
    // 我是猫: 喵喵喵
}

控制台日志

我是适配:哒哒哒
我是猫: 喵喵喵
1.3.1.1.3、中间dubbo生成的适配扩展类
package com.healerjean.proj.study.spi;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Animal$Adaptive implements com.healerjean.proj.study.spi.Animal {
    public void call(java.lang.String arg0, com.alibaba.dubbo.common.URL arg1) {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        
        //获取animal,如果获取不到则选择adaptiveAnimal
        String extName = url.getParameter("animal", "adaptiveAnimal");
        if (extName == null) 
            throw new IllegalStateException("Fail to get extension(com.healerjean.proj.study.spi.Animal) name from url(" + url.toString() + ") use keys([animal])");

        com.healerjean.proj.study.spi.Animal extension = (com.healerjean.proj.study.spi.Animal) 
            ExtensionLoader.getExtensionLoader(com.healerjean.proj.study.spi.Animal.class).getExtension(extName);
        
        extension.call(arg0, arg1);
    }
}

1.3.1.2、实例2

@SPI注解中有value(dog)值,URL中也有具体的值(cat),实现类AdaptiveAnimal上有@Adaptive注解

1.3.1.2.1、示例代码

接口

@SPI(value = "dog") //@SPI指定默认的扩展类
public interface Animal {

    @Adaptive
    void call(String msg, URL url);
}

扩展类

@Adaptive
@Slf4j
public class AdaptiveAnimal implements Animal {

    @Override
    public void call(String msg, URL url) {
      log.info("我是适配:{}", msg);
    }
}


@Slf4j
public class Cat implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是猫: {}", msg);
    }
}


@Slf4j
public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗: {},", msg);
    }
}
1.3.1.3.2、启动测试
@Test
public void testAdaptive2()  {
    ExtensionLoader<Animal> annoimalExtensionLoader =  ExtensionLoader.getExtensionLoader(Animal.class);
    Animal adaptiveExtension = annoimalExtensionLoader.getAdaptiveExtension();
    URL url = URL.valueOf("test://localhost/test?animal=cat");
    adaptiveExtension.call("哒哒哒", url);
}

控制台日志:

我是适配:哒哒哒
1.3.1.2.3、中间dubbo不会生成

不会帮我们生成,因为,这个时候使用了注解@Adaptive,则这个类就是适配扩展类, 那如果有多个类给注解了呢,则只会选中一个,具体是那个毫无意义,

1.3.1.4、实例3

@SPI 注解中有 value 值,实现类上没有 @Adaptive 注解,方法上的 @Adaptive 注解,注解中的 value 与链接中的参数的 key 一致,链接中的 key 对应的 value 就是 spi 中的 name,获取相应的实现类。

1.3.1.4.1、实例代码

接口:

@SPI(value = "adaptiveAnimal") //@SPI指定默认的扩展类
public interface Animal {

    @Adaptive(value = {"aname", "bname"})
    void call(String msg, URL url);

}

扩展类

@Slf4j
public class AdaptiveAnimal implements Animal {

    @Override
    public void call(String msg, URL url) {
      log.info("我是适配:{}", msg);
    }
}

@Slf4j
public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗: {},", msg);
    }
}

@Slf4j
public class Cat implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是猫: {}", msg);
    }
}
1.3.1.4.2、启动测试
/**
 * SPI注解中有value值,实现类上没有@Adaptive注解,方法上的@Adaptive注解,注解中的value与链接中的参数的key一致
 */
@Test
public void testAdaptive3()  {
    ExtensionLoader<Animal> annoimalExtensionLoader =  ExtensionLoader.getExtensionLoader(Animal.class);
    Animal adaptiveExtension = annoimalExtensionLoader.getAdaptiveExtension();
    URL url = URL.valueOf("test://localhost/test?aname=dog");
    adaptiveExtension.call("汪汪汪", url);
    
     url = URL.valueOf("test://localhost/test?bname=dog");
    adaptiveExtension.call("汪汪汪", url);
}

控制台日志:

我是狗: 汪汪汪,
我是狗: 汪汪汪
1.3.1.3.3、中间dubbo生成的适配扩展类
package com.healerjean.proj.study.spi;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Animal$Adaptive implements com.healerjean.proj.study.spi.Animal {
	public void call(java.lang.String arg0, com.alibaba.dubbo.common.URL arg1) {
		if (arg1 == null) throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg1;
        
        //获取url路径中key为name的值,获取不到的话,就选中adaptiveAnimal
		String extName = url.getParameter("name", "adaptiveAnimal");
		if (extName == null) throw new IllegalStateException("Fail to get extension(com.healerjean.proj.study.spi.Animal) name from url(" + url.toString() + ") use keys([name])");
		com.healerjean.proj.study.spi.Animal extension = (com.healerjean.proj.study.spi.Animal) ExtensionLoader.getExtensionLoader(com.healerjean.proj.study.spi.Animal.class).getExtension(extName);
		extension.call(arg0, arg1);
	}
}

1.3.2、多个方法

1.3.2.1、示例代码

@SPI("adaptiveAnimal")
public interface Animal {

    @Adaptive(value = {"aname", "bname"})
    void call(String msg, URL url);

    @Adaptive(value = {"a", "b"})
    void call2(String msg, URL url);

    void call3(String msg);

}

@Slf4j
public class AdaptiveAnimal implements Animal {

    @Override
    public void call(String msg, URL url) {
      log.info("我是适配:{}", msg);
    }

    @Override
    public void call2(String msg, URL url) {

    }

    @Override
    public void call3(String msg) {

    }
}


@Slf4j
public class Cat implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是猫: {}", msg);
    }

    @Override
    public void call2(String msg, URL url) {

    }

    @Override
    public void call3(String msg) {

    }
}


@Slf4j
public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗: {},", msg);
    }

    @Override
    public void call2(String msg, URL url) {

    }

    @Override
    public void call3(String msg) {

    }
}

1.4.1.2、中间代码生成(只有中间代码生成才会出现下面的情况)

会发现 call3 其实是不支持的,说明适配代码必须加 @Adaptive,否则没有意义

package com.healerjean.proj.study.spi.d02_dubbo.api;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Animal$Adaptive implements com.healerjean.proj.study.spi.d02_dubbo.api.Animal {
	public void call(java.lang.String arg0, org.apache.dubbo.common.URL arg1) {
		if (arg1 == null) throw new IllegalArgumentException("url == null");
		org.apache.dubbo.common.URL url = arg1;
		String extName = url.getParameter("aname", url.getParameter("bname", "adaptiveAnimal"));
		if (extName == null) throw new IllegalStateException("Failed to get extension (com.healerjean.proj.study.spi.d02_dubbo.api.Animal) name from url (" + url.toString() + ") use keys([aname, bname])");
		com.healerjean.proj.study.spi.d02_dubbo.api.Animal extension = (com.healerjean.proj.study.spi.d02_dubbo.api.Animal) ExtensionLoader.getExtensionLoader(com.healerjean.proj.study.spi.d02_dubbo.api.Animal.class).getExtension(extName);
		extension.call(arg0, arg1);
	}

    
	public void call2(java.lang.String arg0, org.apache.dubbo.common.URL arg1) {
		if (arg1 == null) throw new IllegalArgumentException("url == null");
		org.apache.dubbo.common.URL url = arg1;
		String extName = url.getParameter("a", url.getParameter("b", "adaptiveAnimal"));
		if (extName == null) throw new IllegalStateException("Failed to get extension (com.healerjean.proj.study.spi.d02_dubbo.api.Animal) name from url (" + url.toString() + ") use keys([a, b])");
		com.healerjean.proj.study.spi.d02_dubbo.api.Animal extension = (com.healerjean.proj.study.spi.d02_dubbo.api.Animal) ExtensionLoader.getExtensionLoader(com.healerjean.proj.study.spi.d02_dubbo.api.Animal.class).getExtension(extName);
		extension.call2(arg0, arg1);
	}
    
        
	public void call3(java.lang.String arg0) {
		throw new UnsupportedOperationException("The method public abstract void com.healerjean.proj.study.spi.d02_dubbo.api.Animal.call3(java.lang.String) of interface com.healerjean.proj.study.spi.d02_dubbo.api.Animal is not adaptive method!");
	}
}

1.4、@Activate

1、group 和搜索到此类型的实例进行比较,如果 group 能匹配到,就是我们选择的,也就是在此条件下需要激活的

2、value 是参数是第二层过滤参数(第一层是通过group),在 group 校验通过的前提下,如果URL中的参数(k)与值(v)中的参数名同@Activate中的value值一致或者包含,那么才会被选中。相当于加入了value后,条件更为苛刻点,需要URL中有此参数并且,参数必须有值。

3、order 参数对于同一个类型的多个扩展来说,order值越小,优先级越高。

接着上面的代码

@Activate(group = "default_group", value = "valueAc")
@Slf4j
public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗,发出叫声: {},", msg);
    }

}

@Test
public void getActive() {
    ExtensionLoader<Animal> annoimalExtensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
    URL url = URL.valueOf("test://localhost/test");
    List<Animal> list = annoimalExtensionLoader.getActivateExtension(url, new String[]{}, "default_group");
    list.stream().forEach(System.out::println); //null

    log.info("-----------------");
    
    url = URL.valueOf("test://localhost/test?valueAc=fasdjafjdklj");
    list = annoimalExtensionLoader.getActivateExtension(url, new String[]{}, "default_group");
    list.stream().forEach(System.out::println); //com.healerjean.proj.study.spi.Dog@59fd97a8
}

1.5、WrapperClass

如果检测到是一个包装类,则不会放入 cachedNamescachedClasses中,那么怎么才符合条件呢,就是说有一个构造器,传入的参数是接口

//包装类集合
private Set<Class<?>> cachedWrapperClasses;

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    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.");
    }
    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 if (isWrapperClass(clazz)) {
        //如果是一个包装类,则放到 cachedWrapperClasses中去
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    } else {
        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 " + resourceURL);
                }
            }
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            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());
                }
            }
        }
    }
}




private boolean isWrapperClass(Class<?> clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false;
    }
}

1.4.1、实例说明

1.4.1.1、示例代码

接口

@SPI
public interface Animal {

    @Adaptive(value = "name")
    void call(String msg, URL url);

}

包装类CAT ,扩展类DOG

@Slf4j
public class Cat implements Animal {

    private Animal animal;

    public Cat() {
    }

    public Cat(Animal animal) {
        animal = animal;
    }

    @Override
    public void call(String msg, URL url) {
        log.info("我是猫,发出叫声: {},", msg);
    }

}



public class Dog implements Animal {

    @Override
    public void call(String msg, URL url) {
        log.info("我是狗,发出叫声: {},", msg);
    }

}

@Test
public void load()  {
    ExtensionLoader<Animal> annoimalExtensionLoader =  ExtensionLoader.getExtensionLoader(Animal.class);
    log.info("#######################################");
    Animal dog = annoimalExtensionLoader.getExtension("dog"); 
    dog.call("旺旺旺", null); //我是猫,发出叫声: 旺旺旺  ,说明返回的是cat扩展类,而不是dog,具体看下面的代码分析
    
    
    Animal cat = annoimalExtensionLoader.getExtension("cat"); //抛出异常,不存在该扩展类,就是说
    cat.call("我是一只猫", null);
    log.info("#######################################");
}

####

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, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);
        //如果发现包装类的话,则返回的是cat,cat构造器中传入的是dog
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            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、SPI源码分析

2.1、获取扩展点加载 ExtensionLoader

所有扩展必须加上 @SPI 注解才能通过 ExtensionLoader加载。 对于一个扩展点只会加载一次,生成一个 ExtensionLoader

通过ExtensionLoader.getExtensionLoader(class type)传入一个spi扩展返回一个ExtensionLoader实例,如下。

ExtensionLoader<Robot> extensionLoader =  ExtensionLoader.getExtensionLoader(Robot.class);  
// static 变量,所有的扩展点都只会加载一次,然后就会放到这里。相当于缓存,type为key的值,扩展点就是value
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();



@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!");
  }

  //从缓存中取,如果缓存中没有 则,new一个扩展点加载
  ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    
  //如果缓存中没有 则,new一个扩展点加载,并将它放到缓存中去,这个type,就是key的值,重点我们再看这个 new ExtensionLoader<T>(type)
  if (loader == null) {
    EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
    loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
  }
  return loader;
}



//验证 是否有@SPI注解
private static <T> boolean withExtensionAnnotation(Class<T> type) {
  return type.isAnnotationPresent(SPI.class);
}

首次进入的话,typeRobot.class肯定不等扩展点工厂,然后通过3元表达式可以看到,我们需要先获取扩展点工厂加载(ExtensionFactory

这样的话,扩展点工厂的属性objectFactory,就是null,而我们传入的Robot.class new出来的扩展加载器,中的属性objectFactory,则是通过扩展点工厂获取的适配扩展点工厂,

接着我们看如果获取适配加载点,以及适配加载点是什么getAdaptiveExtension()


private final Class<?> type;
private final ExtensionFactory objectFactory;



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

2.1.1、获取适配扩展点

Dubbo中的扩展点均有多个实现,而框架设计原则又让我们针对接口编程而不是实现,这就需要在运行期才能决定具体使用哪个扩展实现类。

Dubbo提供了Adpative注解,让我们自行决定究竟是自己提供扩展的适配还是由 Dubbo 来帮我们生成动态适配。

后面会给出2个自定义扩展实现,一个是我们自定义适配,另一个是Dubbo提供动态适配。

适配扩展点
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
@SPI
public interface ExtensionFactory {

    <T> T getExtension(Class<T> type, String name);

}
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}
public class SpringExtensionFactory implements ExtensionFactory {

    private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();

    public static void addApplicationContext(ApplicationContext context) {
        contexts.add(context);
    }

    public static void removeApplicationContext(ApplicationContext context) {
        contexts.remove(context);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {
        for (ApplicationContext context : contexts) {
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
        return null;
    }

}
public class Holder<T> {

    private volatile T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

}
//当前对象中的缓存的适配扩展点
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();


//获取适配扩展点,如果没有的话,就创建,然后放到当前对象的cachedAdaptiveInstance 中去
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
  //从当前对象 的缓存属性,扩展点实例中获取一个适配扩展点
  Object instance = cachedAdaptiveInstance.get();
  if (instance == null) {
    if (createAdaptiveInstanceError == null) {
      synchronized (cachedAdaptiveInstance) {
        instance = cachedAdaptiveInstance.get();
        if (instance == null) {
          try {
            // 经过锁流程后还是没有的话,我们就要创建适配扩展点了,创建完成之后,放到当前对象的缓存适配属性cachedAdaptiveInstance中去
            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;
}

2.1.1.1、创建适配扩展点

先执行getAdaptiveExtensionClass() 获取一个适配扩展点的calss对象之后,再通过反射实例化一个对象,然后再通过方能发,injectExtension对适配扩展点对象其中的属性进行注入

//创建适配扩展点
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
      // 先执行getAdaptiveExtensionClass() 获取一个适配扩展点的calss对象之后,再通过反射实例化一个对象,
      // 然后再通过注入扩展方法,injectExtension对适配扩展点对象其中的属性进行注入
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}
//当发现扩展点类class的时候,这里就会赋值
private volatile Class<?> cachedAdaptiveClass = null;


private Class<?> getAdaptiveExtensionClass() {
  //获取所有的扩展点类class
  getExtensionClasses();
  //通过上面的一步正常情况下的话,cachedAdaptiveClass 适配扩展点类就存在了
  if (cachedAdaptiveClass != null) {
    return cachedAdaptiveClass;
  }
  return cachedAdaptiveClass = createAdaptiveExtensionClass();
}


//从当前对象的的属性  缓存类 中获取所有的扩展点(不包含适配扩展点哦,具体看下面loadExtensionClasses的代码)
private Map<String, Class<?>> getExtensionClasses() {
  Map<String, Class<?>> classes = cachedClasses.get();
  if (classes == null) {
    synchronized (cachedClasses) {
      classes = cachedClasses.get();
      if (classes == null) {
        //加载所所有的扩展点,加载完成之后放到当前对象的属性cachedClasses 中
        classes = loadExtensionClasses();
        cachedClasses.set(classes);
      }
    }
  }
  return classes;
}




// 加载扩展类class
private Map<String, Class<?>> loadExtensionClasses() {
    
    cacheDefaultExtensionName();
   
    //将来key就是  我们的配置文件中的name,value就是扩展类class。而下面这些变量就解释了我上面在啊实例中说的目录的问题,不一定是dubb文件夹中
    Map<String, Class<?>> extensionClasses = new HashMap<>();

    //  strategies 指的是扩展类的目录加载策略(选择不同的目录进行加载,这里的for表示对所有的目录 strategy.directory() 进行加载)
    // META-INF/dubbo/internal/
    // META-INF/dubbo/
    // META-INF/services/
    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), 
                      strategy.overridden(), strategy.excludedPackages());
        loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), 
                      strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
    }

    return extensionClasses;
}

//缓存@SPI注解的默认的扩展类名字,存放到 cachedDefaultName
private void cacheDefaultExtensionName() {
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation == null) {
        return;
    }

    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) {
             //如果说@SPI 有 value值,则下面 cachedDefaultName 将来在没有@Adaptive 注解修饰的时候。
            //dubbo帮我们生成适配扩展类的时候用来作为备用选项(当url不存在参数的时候,返回@SPI value所代表的扩展类),
            cachedDefaultName = names[0];
        }
    }
}



}
public interface LoadingStrategy extends Prioritized {

    String directory();

    default boolean preferExtensionClassLoader() {
        return false;
    }

    default String[] excludedPackages() {
        return null;
    }

 
    default boolean overridden() {
        return false;
    }
}



public class DubboInternalLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/internal/";
    }

    @Override
    public int getPriority() {
        return MAX_PRIORITY;
    }
}


public class DubboLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return NORMAL_PRIORITY;
    }

}

public class ServicesLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/services/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return MIN_PRIORITY;
    }

}



image-20201102164707138



private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                           boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
    String fileName = dir + type;
    try {
        Enumeration<java.net.URL> urls = null;
        ClassLoader classLoader = findClassLoader();

        // try to load from ExtensionLoader's ClassLoader first
        if (extensionLoaderClassLoaderFirst) {
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                urls = extensionLoaderClassLoader.getResources(fileName);
            }
        }

        if (urls == null || !urls.hasMoreElements()) {
            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, overridden, excludedPackages);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                     type + ", description file: " + fileName + ").", t);
    }
}




private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                          java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
    try {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            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) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden);
                        }
                    } catch (Throwable t) {
                        IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                     type + ", class file: " + resourceURL + ") in " + resourceURL, t);
    }
}



private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();

private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();


private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                       boolean overridden) throws NoSuchMethodException {
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                                        type + ", class line: " + clazz.getName() + "), class "
                                        + clazz.getName() + " is not subtype of interface.");
    }
    //如果扩展点类上面的注解有 Adaptive ,则不会放到map中,而是给当前属性 cachedAdaptiveClass 赋值(这样的话,一定要记得extensionLoader.getExtension("adaptiveAnimal") 将不能再使用)
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        cacheAdaptiveClass(clazz, overridden);
        //否则如果是一个包装类 类中有同类(构造器)   
    } else if (isWrapperClass(clazz)) {
        cacheWrapperClass(clazz);
    } else {
        //此处相当于作为一个判断,必须有空构造器才可以
        clazz.getConstructor();
        if (StringUtils.isEmpty(name)) {
            // 如果dubbo文件中 没有name,而是直接写了类,则我们通过全类名获取name 
            // 比如com.healerjean.proj.study.spi.Dog 返回是 dog
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
            }
        }

        String[] names = NAME_SEPARATOR.split(name);
        if (ArrayUtils.isNotEmpty(names)) {
            //缓存激活类
            cacheActivateClass(clazz, names[0]);

            // 正常情况下,只有一个name
            for (String n : names) {
                //讲扩展类名 缓存到 cachedNames 
                cacheName(clazz, n);
                //根据名称缓存到 extensionClasses 中
                saveInExtensionClass(extensionClasses, clazz, n, overridden);
            }
        }
    }
}


private void cacheName(Class<?> clazz, String name) {
    if (!cachedNames.containsKey(clazz)) {
        cachedNames.put(clazz, name);
    }
}


private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name, boolean overridden) {
    Class<?> c = extensionClasses.get(name);
    if (c == null || overridden) {
        extensionClasses.put(name, clazz);
    } else if (c != clazz) {
        String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
        logger.error(duplicateMsg);
        throw new IllegalStateException(duplicateMsg);
    }
}
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);
}

2.1.1.2、扩展点通过反射进行实例化

@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
       //执行这个的时候,静态缓存中已经有了扩展点工厂了
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
       // 当前实力缓存中已经有了扩展类的所有属性了,所以这里是从缓存中取出的
        for (String name : loader.getSupportedExtensions()) {
           //通过扩展点加载,加载所有的扩展点实现类,spi,spring
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}
//存放当前对象所有的扩展点实现实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();


@SuppressWarnings("unchecked")
public T getExtension(String name) {
  if (name == null || name.length() == 0)
    throw new IllegalArgumentException("Extension name == null");
  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;
}

// 扩展点实例所有实例的静态属性,缓存
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();


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, clazz.newInstance());
      instance = (T) EXTENSION_INSTANCES.get(clazz);
    }
    //对属性进行注入
    injectExtension(instance);
      
      //如果是一个包装类的需要处理
    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
      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.1.1.2、扩展点进行属性注入

如果是扩展点工厂进来了则,不会注入,因为其什么属性都没有

  private T injectExtension(T instance) {
		
      //如果是扩展点工厂进入则直接返回
        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                //如果不是set方法则返回
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try 
                    //为set属性进行诸如
                    String property = getSetterProperty(method);
                	// 从扩展工厂适配获取set的对象,进行构造
                    //(需要注意的是,如果是dubbo SPI的方式则放回的是一个适配扩展对象,如果是Spring的话,则从Spring工厂中取(property名字的实现类))
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
                    logger.error("Failed to inject via method " + method.getName()
                            + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }

            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

2.1.2、生成适配扩展点

当没有@Adaotive注解的时候,会帮我们自动生成一个新的扩展点类

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //生成适配扩展点
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}


//生成扩展点类,里面会判断是否有@Adaptive注释在方法上,如果没有则报错
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);
    }

 private String createAdaptiveExtensionClassCode() {
        StringBuilder codeBuilder = new StringBuilder();
        Method[] methods = type.getMethods();
        boolean hasAdaptiveAnnotation = false;
        for (Method m : methods) {
            if (m.isAnnotationPresent(Adaptive.class)) {
                hasAdaptiveAnnotation = true;
                break;
            }
        }
        // no need to generate adaptive class since there's no adaptive method found.
        if (!hasAdaptiveAnnotation)
            throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");

        codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
        codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
        codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");

        for (Method method : methods) {
            Class<?> rt = method.getReturnType();
            Class<?>[] pts = method.getParameterTypes();
            Class<?>[] ets = method.getExceptionTypes();

            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
            StringBuilder code = new StringBuilder(512);
            if (adaptiveAnnotation == null) {
                code.append("throw new UnsupportedOperationException(\"method ")
                        .append(method.toString()).append(" of interface ")
                        .append(type.getName()).append(" is not adaptive method!\");");
            } else {
                int urlTypeIndex = -1;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].equals(URL.class)) {
                        urlTypeIndex = i;
                        break;
                    }
                }
                // found parameter in URL type
                if (urlTypeIndex != -1) {
                    // Null Point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
                            urlTypeIndex);
                    code.append(s);

                    s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
                    code.append(s);
                }
                // did not find parameter in URL type
                else {
                    String attribMethod = null;

                    // find URL getter method
                    LBL_PTS:
                    for (int i = 0; i < pts.length; ++i) {
                        Method[] ms = pts[i].getMethods();
                        for (Method m : ms) {
                            String name = m.getName();
                            if ((name.startsWith("get") || name.length() > 3)
                                    && Modifier.isPublic(m.getModifiers())
                                    && !Modifier.isStatic(m.getModifiers())
                                    && m.getParameterTypes().length == 0
                                    && m.getReturnType() == URL.class) {
                                urlTypeIndex = i;
                                attribMethod = name;
                                break LBL_PTS;
                            }
                        }
                    }
                    if (attribMethod == null) {
                        throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
                                + ": not found url parameter or url attribute in parameters of method " + method.getName());
                    }

                    // Null point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
                            urlTypeIndex, pts[urlTypeIndex].getName());
                    code.append(s);
                    s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
                            urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
                    code.append(s);

                    s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
                    code.append(s);
                }

                String[] value = adaptiveAnnotation.value();
                // value is not set, use the value generated from class name as the key
                if (value.length == 0) {
                    char[] charArray = type.getSimpleName().toCharArray();
                    StringBuilder sb = new StringBuilder(128);
                    for (int i = 0; i < charArray.length; i++) {
                        if (Character.isUpperCase(charArray[i])) {
                            if (i != 0) {
                                sb.append(".");
                            }
                            sb.append(Character.toLowerCase(charArray[i]));
                        } else {
                            sb.append(charArray[i]);
                        }
                    }
                    value = new String[]{sb.toString()};
                }

                boolean hasInvocation = false;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
                        // Null Point check
                        String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
                        code.append(s);
                        s = String.format("\nString methodName = arg%d.getMethodName();", i);
                        code.append(s);
                        hasInvocation = true;
                        break;
                    }
                }

                String defaultExtName = cachedDefaultName;
                String getNameCode = null;
                for (int i = value.length - 1; i >= 0; --i) {
                    if (i == value.length - 1) {
                        if (null != defaultExtName) {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation)
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                        } else {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation)
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                            else
                                getNameCode = "url.getProtocol()";
                        }
                    } else {
                        if (!"protocol".equals(value[i]))
                            if (hasInvocation)
                                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                        else
                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
                    }
                }
                code.append("\nString extName = ").append(getNameCode).append(";");
                // check extName == null?
                String s = String.format("\nif(extName == null) " +
                                "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
                        type.getName(), Arrays.toString(value));
                code.append(s);

                s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
                code.append(s);

                // return statement
                if (!rt.equals(void.class)) {
                    code.append("\nreturn ");
                }

                s = String.format("extension.%s(", method.getName());
                code.append(s);
                for (int i = 0; i < pts.length; i++) {
                    if (i != 0)
                        code.append(", ");
                    code.append("arg").append(i);
                }
                code.append(");");
            }

            codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");
            for (int i = 0; i < pts.length; i++) {
                if (i > 0) {
                    codeBuilder.append(", ");
                }
                codeBuilder.append(pts[i].getCanonicalName());
                codeBuilder.append(" ");
                codeBuilder.append("arg").append(i);
            }
            codeBuilder.append(")");
            if (ets.length > 0) {
                codeBuilder.append(" throws ");
                for (int i = 0; i < ets.length; i++) {
                    if (i > 0) {
                        codeBuilder.append(", ");
                    }
                    codeBuilder.append(ets[i].getCanonicalName());
                }
            }
            codeBuilder.append(" {");
            codeBuilder.append(code.toString());
            codeBuilder.append("\n}");
        }
        codeBuilder.append("\n}");
        if (logger.isDebugEnabled()) {
            logger.debug(codeBuilder.toString());
        }
        return codeBuilder.toString();
    }

author