Java中的Spi以及Dubbo中的Spi机制解析

目录

一、Java的SPI

二、Dubbo的SPI

 dubbo的IOC       

  dubbo的AOP

 动态编译


        SPI 全称为 Service Provider Interface,是一种服务发现机制。本质是由将接口的实现类的全限定名配置在文件当中,由服务加载,这样可以在运行时,动态的为接口加载实现类。

        举个例子,我们在用JDBC连接数据库时,创建连接就能直接获取到Mysql或者Oracle,Java时如何调用到相对应的驱动呢?

        我们将连接数据库看作一个扩展点,其他数据都是该点的实现,当我们需要连接相对应的的数据库时,Spi会帮助我们发现有哪些实现并加载。

一、Java的SPI

        按照惯例,先写一个例子,先定义一个接口EatFood,其次创建它的两个实现类,KFC和McDonald,然后META-INF/services下创建一个EatFood的全限定名的文件,里面存放着两个实现类的全限定名,然后我们可以在执行一下代码

public interface EatFood {
    void eat();
}
public class KFC implements EatFood{
    @Override
    public void eat() {
        System.out.println("吃肯德基");
    }
}
public class McDonald implements EatFood{
    @Override
    public void eat() {
        System.out.println("吃麦当劳");
    }
}
com.yxl.KFC
com.yxl.McDonald

测试代码

public class JavaSpiTest {
    @Test
    public void test(){
        ServiceLoader<EatFood> eatFoodServiceLoader = ServiceLoader.load(EatFood.class);
        Iterator<EatFood> iterator = eatFoodServiceLoader.iterator();
        while(iterator.hasNext()){
            EatFood eatFood = iterator.next();
            eatFood.eat();
        }
    }
}

很简单的一段代码,这时我们来看一下源码的流程

        点进 ServiceLoader.load(EatFood.class)中的load,可以到是为传进去的这个拓展点创建了一个服务加载器

public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

        然后我们点进eatFoodServiceLoader.iterator()中的iterator,然后点进knownProviders.hasNext()

    public Iterator<S> iterator() {
        return new Iterator<S>() {

            Iterator<Map.Entry<String,S>> knownProviders
                = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

        可以看到这样一段代码,然后点进hasNextService()

public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        上图的方法就是判断是否有相对应的服务,可以看到中间的代码

        String fullName = PREFIX + service.getName();

        前者PREFIX就是就是我们刚刚创建的目录路径

  service.getName()就是获取我们传进来的EatFood的全限定名

下面这段代码则是会解析该文件pending = parse(service, configs.nextElement());

然后我们再回到刚刚的迭代器,可以看到如果返回了True,继续获取对应的服务

 然后在这边可以看到通过反射创建该服务的实例

         这就是java的Spi的流程,下面我们讲一讲Dubbo的Spi


二、Dubbo的SPI

        上面我们讲了Java的SPI,机智的小伙伴们肯定已经发现了缺点,就是没有办法选取特定的实例,只能通过迭代的方式进行操作。Dubbo中并没有使用Java的SPI,而是重新编写了一套逻辑,可以满足我们指定相对应的实例。

        先上一个例子

@SPI
public interface Robot {

	void sayHello();
}
public class Bumblebee implements Robot {

	@Override
	public void sayHello() {
		System.out.println("Hello, I am Bumblebee.");
	}

}

   

public class DubboSpiTest {

    //测试dubbo spi机制
    @Test
    public  void sayHello() throws Exception {
        //1、获得接口的ExtentionLoader
        ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
        //2、根据指定的名字获(key)取对应的实例
        Robot robot = extensionLoader.getExtension("optimusPrime");
        robot.sayHello();
        /*Robot optimusPrime = extens
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值