(3)dubbo-SPI 约定大于配置

一. 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);

输出结果如下:
在这里插入图片描述

  1. 从Dubbo的SPI的代码来看, 大致分为两步, 第一步loader接口, 第二步getExtension按需所取.
  2. @SPI注解表示 该接口为dubbo spi标识, @SPI(“defaulltName”) 该defaulltName为getExtension(“true”)时 返回的默认类
  3. 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方法主要有三个方面

  1. 创建实现接口的对象
  2. IOC属性注入 支持spring和dubbo的spi
  3. 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方法.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值