一. Java SPI
public interface Animals {
String sayAnimal();
}
public class MonkeyAnimal implements Animals {
@Override
public String sayAnimal() {
return "monkey";
}
}
resources下建META-INF/services/com.example.consumer.spi.Animals (接口的全限命名)
文件内容是接口的实现类全限命名(可以多个)
com.example.consumer.spi.MonkeyAnimal
public static void main(String[] args) {
ServiceLoader<Animals> animals = ServiceLoader.load(Animals.class);
for (Animals animal : animals) {
System.out.println(animal.sayAnimal());
}
}
输出结果如下:
Java SPI有明显的缺点:
无法按需所取
只能实例化实现接口的类 缺少扩展性
二. Dubbo SPI 基础使用
@SPI("apple")
public interface Fruits {
String sayFruit();
}
public class AppleFruit implements Fruits {
@Override
public String sayFruit() {
return "dubbo spi apple";
}
}
@Extension("banana")
public class BananaFruit implements Fruits {
@Override
public String sayFruit() {
return "dubbo spi banana";
}
}
resources下建META-INF/dubbo/com.example.consumer.spi.Fruits (接口的全限命名)
apple=com.example.consumer.spi.AppleFruit
com.example.consumer.spi.BananaFruit
ExtensionLoader<Fruits> fruitExtension = ExtensionLoader.getExtensionLoader(Fruits.class);
Fruits apple = fruitExtension.getExtension("apple");
Fruits banana = fruitExtension.getExtension("banana");
Fruits defaultFruit = fruitExtension.getExtension("true");
System.out.println(apple);
System.out.println(banana);
System.out.println(defaultFruit);
输出结果如下:
- 从Dubbo的SPI的代码来看, 大致分为两步, 第一步loader接口, 第二步getExtension按需所取.
- @SPI注解表示 该接口为dubbo spi标识, @SPI(“defaulltName”) 该defaulltName为getExtension(“true”)时 返回的默认类
- BananaFruit上面的@Extension(“banana”) 表示xml里的key 这个注解已经被@Deprecated了,已经不推荐使用了
三. wrapper装饰器
这个时候如果我们想就着实现类做一些扩展的前后逻辑. 这个时候我们需要装饰器.
public class FruitWrapper implements Fruits {
private Fruits fruits;
public FruitWrapper(Fruits fruits) {
this.fruits = fruits;
}
@Override
public String sayFruit() {
System.out.println("wrapper...");
return fruits.sayFruit();
}
}
resources下META-INF/dubbo/com.example.consumer.spi.Fruits
加一行com.example.consumer.spi.FruitWrapper
接下来运行一下:
ExtensionLoader<Fruits> fruitExtension = ExtensionLoader.getExtensionLoader(Fruits.class);
Fruits apple = fruitExtension.getExtension("apple");
Fruits banana = fruitExtension.getExtension("banana");
Fruits defaultFruit = fruitExtension.getExtension("true");
System.out.println(apple.sayFruit());
System.out.println(banana.sayFruit());
System.out.println(defaultFruit.sayFruit());
输出结果如下:
这个wrapper类似于AOP对实现类进行了逻辑添加
四. @Adaptive使用与原理
ExtensionLoader的getExtension方法主要有三个方面
- 创建实现接口的对象
- IOC属性注入 支持spring和dubbo的spi
- wrapper装饰
修改一下Fruits接口 使其支持适配
@SPI
public interface Fruits {
@Adaptive
String sayFruit(URL url);
}
新建一个Person @SPI
@SPI
public interface Person {
Fruits getFruit();
}
Person的实现类
public class NewPerson implements Person {
public Fruits fruits;
public void setFruits(Fruits fruits) {
this.fruits = fruits;
}
@Override
public Fruits getFruit() {
System.out.println("i am new person");
return fruits;
}
}
创建和编辑 resources/META-INF/dubbo/com.example.consumer.spi.Person文件
main方法执行, 去执行注入的fruits变量的内部方法.
ExtensionLoader<Person> personExtension = ExtensionLoader.getExtensionLoader(Person.class);
Person newPerson = personExtension.getExtension("newPerson");
URL url = new URL("dubbo", "localhost", 8080);
// 指定fruits用哪个实例
url.addParameter("fruits", "apple");
System.out.println(newPerson.getFruit());
System.out.println(newPerson.getFruit().sayFruit(url));
运行结果如下:
NewPerson这个类, 我们声明了一个Fruits变量, 当dubbo去创建NewPerson时候会根据set方法, 找到这个变量的type和名字, 也就是Fruits和fruits, 先去Spring的IOC容器查找, 先byType后byName, 没有之后去SPI的IOC入容器找, AdaptiveExtensionFactory会去构建Fruits的适配类, 注入进来的也就是一个适配类, AdaptiveClassCodeGenerator的generate方法会生成执行的逻辑,如下
package com.example.consumer.spi;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Fruits$Adaptive implements com.example.consumer.spi.Fruits {
public java.lang.String sayFruit(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("fruits", "apple");
if(extName == null) throw new IllegalStateException("Failed to get extension (com.example.consumer.spi.Fruits) name from url (" + url.toString() + ") use keys([fruits])");
com.example.consumer.spi.Fruits extension = (com.example.consumer.spi.Fruits)ExtensionLoader.getExtensionLoader(com.example.consumer.spi.Fruits.class).getExtension(extName);
return extension.sayFruit(arg0);
}
}
从上面这个生成逻辑来看, 执行适配的方法时候, 回去url里找, 也就是getParameter, 最终执行的就是apple这个实现类的sayFruit方法.