【dubbo系列】SPI机制源码解析

​ SPI 全称为 Service Provider Interface,是一种服务发现机制。此机制在dubbo中大量使用。以至于dubbo框架的及其灵活。

​ dubbo SPI 源码地址:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html

1.java SPI源码解析

1.1 SPI使用场景

​ dubbo官方中有java SPI的示例,但是初始并不是特别的理解。或者根本不理解这种设计的好处。直到看到mysql驱动包才恍然大悟。

​ java官方爸爸想连接数据库,但是连接数据库就要有数据库驱动,但是流行的数据库太多了,难道要一一实现么?这样太费事了。然后jdk就创建了个Driver接口,此接口里面定义了如下方法:
在这里插入图片描述
​ 看不懂方法是干啥的没关系,反正不是普通开发人员来处理的。但是,如此就定义了标准,也就是说,如果想要java来连接数据库,那就厂商或者想连接的开发人员自己去实现此接口就好。但是如果实现的多了java就不知道如何选择了。比如,加了mysql的包,也加了oracle的包,或者自己也实现了这个接口,在多接口的时候,如何选择就成了问题。这个时候SPI机制就有了作用了。

​ 既然这样,如果要连接mysql数据库的话, 那么mysql驱动包里,一定要实现此接口。
在这里插入图片描述
​ 如上发现,mysql包中确实是有实现Driver接口。内容如下。

​ com.mysql.cj.jdbc.Driver

1.2 java SPI源码解析

1.2.1 示例

​ 假如有如下接口

public interface Robot {
    void sayHello();
}

接下来定义两个实现类,分别为 OptimusPrime 和 Bumblebee。

public class OptimusPrime implements Robot {
    
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

public class Bumblebee implements Robot {

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

接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 org.apache.spi.Robot。文件内容为实现类的全限定的类名,如下:

org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee

public class JavaSPITest {

    @Test
    public void sayHello() throws Exception {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        System.out.println("Java SPI");
        for(Robot service : services) {
            service.sayHello();
        }
    }
}

输出:

Java SPI

Hello, I am Optimus Prime.

Hello, I am Bumblebee.

1.2.2解析

​ 原本以为内容加载是在load方法中处理,但是找了一圈也没找到个啥。到最后却发现在for循环中处理的。可能这样看感觉很蒙,很简单的for循环能有多少内容呀?

    public static void main(String[] args) {
        ServiceLoader<Robot> services = ServiceLoader.load(Robot.class);
        Iterator var2 = services.iterator();

        while(var2.hasNext()) {
            Robot service = (Robot)var2.next();
            service.sayHello();
        }

    }

​ 代码经过反编译,却变成了这样。那一个迭代器,大家都知道的,能有啥内容呢?ServiceLoad对迭代器中的内容进行了重写。在执行services.iterator();这段代码的时候,是执行如下代码。

​ 这里就涉及到了Iterableiterator这两个类的内容了,详细看这篇博文,写的挺详细的https://www.cnblogs.com/litexy/p/9744241.html

   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();
            }

        };
    }

​ 那么怎么解析的,就只需要关注hasNext方法和next方法就好了。

1.2.3hasNext方法解析

 public boolean hasNext() {
     		//访问控制上下文 为null
            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 {
                    //PREFIX 是META-INF/services/ 这个值是固定死的
                    // service 是设置类的全名,包括路径名
                    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;
        }

1.2.4 next方法解析

            public S next() {
                
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                //第一次走这里
                return lookupIterator.next();
            }
        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
    		// 获取值,这个在hasNext的时候就设置好了。
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                // 获取类
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                // 创建类
                S p = service.cast(c.newInstance());
                // 将此类保存到缓存中
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

猜你喜欢

转载自blog.csdn.net/qq_30285985/article/details/106414637