前言

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 SPI 与 Dubbo 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