SPI原理解析一

SPI(service provider Interfaces),对于Java中的实现SPI类ServiceLoader来讲,就是Java类库里的代码能够调用外部实现类的代码。听起来是不是很抽象。那从原理上来讲如何实现呢?内部调用外部实现类又有什么用呢?在这里我先回答第二个问题:

Q:内部调用外部实现类又有什么用?
A: 我认为有下面两点原因

  • 试想JDK提供一个接口interface Driver{},但是它不想自己实现,因为没有一个固定的实现方式,比如mysql中的数据库驱动加载。于是这个接口留给实现者实现。然后JDK在内部为执行一系列的接口方法(注意这个接口已经被外部类实现,至于如何加载外部类,这就是JDK里的ServiceLoader做的事情了),就能提供灵活的实现方式。
  • 运用上面的思想,我们可以实现A模块引用B模块,A可以调用B中的任何函数,且B模块也能实现调用A模块的函数。实现方式是B(可以类比上述JDK)提供一个接口Interface IB,然后让A模块去实现这个接口。最后通过SPI技术,B能够加载A中的实现类,从而第调用了A中实现类的函数

Q:SPI技术原理是什么呢?
A:运用类加载技术,根据上面想法,我们可知关键点在于B如何加载A中的实现类的。从系统思维角度来讲A引用了B,B就肯定会获得A的某些信息。废话不说,直接上如何实现这样场景的代码:
先看项目路径


image.png

child1的实现:
定义接口

package spi;
public interface DemoService {
    public String sayHi(String msg);
}

META-INF.services/spi.demoService写如下代码,表示实现类的路径,实现类必须要按照该字段定义的路径来

spi.ChineseDemoServiceImpl
spi.EnglishDemoServiceImpl

spiMain.java:该类是实现child1调用child2的重要类

package spi;

import java.util.Iterator;
import java.util.ServiceLoader;

public class SpiMain {

    public static void getConnection(){
        ServiceLoader serviceLoader = ServiceLoader.load(DemoService.class);
        Iterator services = serviceLoader.iterator();
        while (services.hasNext()) {
            DemoService service = services.next();
            System.out.println(service.sayHi("world"));
        }
    }

}

我们需要讲child1模块打包成.jar格式放到child2模块中。
child2:
下面是两个接口实现类,当然我也可以在这个类里面做其他事情,以便child1模块可以更多的调用
ChineseDemoServiceImpl.java

package spi;

public class ChineseDemoServiceImpl implements DemoService {

    @Override
    public String sayHi(String msg) {
        return "你好" + msg;
    }
}

EnglishDemoServiceImpl.java

package spi;
public class EnglishDemoServiceImpl implements DemoService {
    @Override
    public String sayHi(String msg) {
        return "hello" + msg;
    }
}

child2Main.java

package spi;
public class Child2Main {
    public static void main(String[] args) {
        SpiMain.getConnection();//调用child1的函数,初始化自己提供的服务实现类
    }
}

从以上代码,我们就能比较具体的看到SPI的作用了。也能回答之前提出的问题了,是通过在文件里指定路径名的方式,去搜索的实现类。但是问题在于ServiceLoader是JDK里面的呀,是通过Extension ClassLoader加载的呀,这个加载器如何去加载应该有App ClassLoader加载的.class文件呢?原来其引入了一个线程上下文类加载器(该加载器是App ClassLoader),强制破坏了双亲委派模型层层加载的原则,在ServiceLoader里直接请求上下文加载器去加载指定路径的.class文件。

好了,这一节先说到这里,我会在原理解析二中,详细展开ServiceLoader的源代码。

源码github:https://github.com/zhouyueyuedsf/spidemo

你可能感兴趣的:(SPI原理解析一)