前言
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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MTOIpGAu-1594897494549)(https://raw.githubusercontent.com/HealerJean/HealerJean.github.io/master/blogImages/image-20200627155645060.png)]
1.1.3、Main方法测试
@Slf4j
public class JavaSPITest {
@Test
public void sayHello() {
ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
log.info("Java SPI--------");
serviceLoader.forEach(Robot::sayHello);
}
}
1.1.3.1、控制台日志
2020-06-27 15:58:02 INFO -[ ]- Java SPI-------- com.healerjean.proj.study.spi.java.JavaSPITest.sayHello[21]
2020-06-27 15:58:02 INFO -[ ]- Hello, I am Optimus Prime. com.healerjean.proj.study.spi.OptimusPrime.sayHello[16]
2020-06-27 15:58:02 INFO -[ ]- Hello, I am Bumblebee. com.healerjean.proj.study.spi.Bumblebee.sayHello[17]
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.OptimusPrime
bumblebee = com.healerjean.proj.study.spi.Bumblebee
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6jkaSvC-1594897494551)(https://raw.githubusercontent.com/HealerJean/HealerJean.github.io/master/blogImages/image-20200627160249742.png)]
1.2.3、Main方法启测试
@Slf4j
public class DubboSPITest {
@Test
public void sayHello() {
ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
log.info("dubbo SPI--------");
log.info("----------------------------");
log.info("从配置文件中获取--------");
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
log.info("----------------------------");
log.info("{}", extensionLoader.getSupportedExtensions());
}
1.2.3.1、控制台日志
2020-06-27 16:04:19 INFO -[ ]- using logger: com.alibaba.dubbo.common.logger.slf4j.Slf4jLoggerAdapter com.alibaba.dubbo.common.logger.LoggerFactory.[]
2020-06-27 16:04:19 INFO -[ ]- dubbo SPI-------- com.healerjean.proj.study.spi.dubbo.DubboSPITest.sayHello[21]
2020-06-27 16:04:19 INFO -[ ]- ---------------------------- com.healerjean.proj.study.spi.dubbo.DubboSPITest.sayHello[22]
2020-06-27 16:04:19 INFO -[ ]- 从配置文件中获取-------- com.healerjean.proj.study.spi.dubbo.DubboSPITest.sayHello[23]
2020-06-27 16:04:20 INFO -[ ]- Hello, I am Optimus Prime. com.healerjean.proj.study.spi.OptimusPrime.sayHello[16]
2020-06-27 16:04:20 INFO -[ ]- Hello, I am Bumblebee. com.healerjean.proj.study.spi.Bumblebee.sayHello[17]
2020-06-27 16:04:20 INFO -[ ]- ---------------------------- com.healerjean.proj.study.spi.dubbo.DubboSPITest.sayHello[28]
2020-06-27 16:04:20 INFO -[ ]- ["bumblebee","optimusPrime"] com.healerjean.proj.study.spi.dubbo.DubboSPITest.sayHello[30]
1.3、@Adaptive 注解使用
在类,以及方法上调用。定义了 注解的方法,参数列表中一定要有类型为 URL 的参数。
value 是个字符数组,通过该属性从 URL 中获取扩展名,来决定使用哪个扩展。分为几种情况:
1.设置了 value,且从 URL 中找到了对应的扩展名,则使用该扩展;
2.设置了 value,但从 URL 中找不到扩展名,则使用默认的扩展,即@SPI 中配置的 value,还是找不到则抛出异常;
3.未设置 value,则根据接口名生成 value,比如接口 Animal生成 value = “animal”。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
}
1.3.1、实例说明
1.3.1.1、实例1
@SPI注解中有value值
1.3.1.1.1、示例代码
接口
@SPI(value = "adaptiveAnimal")
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 test1() {
ExtensionLoader<Animal> annoimalExtensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
Animal adaptiveExtension = annoimalExtensionLoader.getAdaptiveExtension();
//1、 @SPI注解中有value值,URL中没有具体的值
URL url = URL.valueOf("test://localhost/test");
adaptiveExtension.call("哒哒哒", url); //我是适配动物,发出叫声:哒哒哒
//2、@SPI注解中有value值,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")
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 test3() {
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、实例4
@SPI注解中有value值,实现类上没有@Adaptive注解,方法上的@Adaptive注解,注解中的value与链接中的参数的key一致,链接中的key对应的value就是spi中的name,获取相应的实现类。
1.3.1.4.1、实例代码
接口:
@SPI(value = "adaptiveAnimal")
public interface Animal {
@Adaptive(value = "name")
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、启动测试
@Test
public void test4() {
ExtensionLoader<Animal> annoimalExtensionLoader = ExtensionLoader.getExtensionLoader(Animal.class);
Animal adaptiveExtension = annoimalExtensionLoader.getAdaptiveExtension();
URL url = URL.valueOf("test://localhost/test?name=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、在类上加上@Adaptive注解的类,是最为明确的创建对应类型Adaptive类。所以他优先级最高。
2、
@SPI
注解中的value是默认值,如果通过URL获取不到关于取哪个类作为Adaptive类的话,就使用这个默认值,当然如果URL中可以获取到,就用URL中的。3、方法上的
@Adaptive
注解,注解中的value
与链接中的参数的key
一致,链接中的key
对应的value
就是spi
中的name
,获取相应的实现类。
1.4、@Activate
1、根据
loader.getActivateExtension中
的group
和搜索到此类型的实例进行比较,如果group能匹配到,就是我们选择的,也就是在此条件下需要激活的2、
@Activate
中的value
是参数是第二层过滤参数(第一层是通过group
),在group校验通过的前提下,如果URL中的参数(k)与值(v)中的参数名同@Activate
中的value
值一致或者包含,那么才会被选中。相当于加入了value
后,条件更为苛刻点,需要URL中有此参数并且,参数必须有值。3、
@Activate
的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.4、WrapperClass
如果检测到是一个包装类,则不会放入
cachedNames
和cachedClasses
中,那么怎么才符合条件呢,就是说有一个构造器,传入的参数是接口
//包装类集合
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);
if (loader == null) {
//如果缓存中没有 则,new一个扩展点加载,并将它放到缓存中去,这个type,就是key的值
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);
}
首次进入的话,
type
是Robot.class
肯定不等扩展点工厂,然后通过3元表达式可以看到,我们需要先获取扩展点工厂加载(ExtensionFactory
),,这样的话,扩展点工厂的属性objectFactory
,就是null,而我们传入的Robot.class
,中的属性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;
}
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
// 加载扩展类class
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
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));
}
//如果说@SPI 有 value值,则下面 cachedDefaultName 将来在没有@Adaptive 注解修饰的时候。dubbo帮我们生成适配扩展类的时候用来作为备用选项(当url不存在参数的时候,返回@SPI value所代表的扩展类),
if (names.length == 1) cachedDefaultName = names[0];
}
}
//将来key就是 我们的配置文件中的name,value就是类。而下面这些变量就解释了我上面在啊实例中说的目录的问题,不一定是dubb文件夹中
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
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) {
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} 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);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load 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) 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.");
}
//如果扩展点类上面的注解有 Adaptive ,则不会放到map中,而是给当前属性 cachedAdaptiveClass 赋值
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)) {
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) {
//如果dubbo文件中 没有name,而是直接写了类,则我们通过全类名获取name 比如com.healerjean.proj.study.spi.Dog 返回是 dog
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);
}
// 正常情况下,只有一个name
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());
}
}
}
}
}
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);
}
}
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
2.1.1.2、扩展点进行属性注入
如果是扩展点工厂进来了则,不会注入,因为其什么属性都没有
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
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();
}