Dubbo SPI 、服务暴露、服务引入源码解析

Dubbo的SPI

SPI什么是SPI,SPI全称为Service Provider Interface,是一种服务发现机制,SPI的本质是将接口实现类的全限定名配置到文件中,并由服务器加载读取配置文件,加载实现类,这样可以在运行时,动态为接口替换实现类,正因此特性,我们可以很容易的通过SPI机制为我们的程序提供扩展功能,SPI机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重的模块。

1、Dubbo SPI

先看一下java原生的SPI,用到了反射机制,跟Spring的IOC里的一样,通过全限定类名,在程序运行阶段,在内存中创建对象 newInstance。

创建一个接口类和两个实现类

public interface User {
    public void service();
}


public class UserOneImpl implements User {
    public void service(){
        System.out.println("实现类一");
    }
}


public class UserTwoImpl implements User {
    public void service(){
        System.out.println("实现类二");
    }
}

在resources目录下创建一个目录,META-INF.services

在这个目录下把,接口的全限定类名

com.jd.service.User  ,里面配置内容是接口实现类的全限定类名

com.jd.service.impl.UserOneImpl
com.jd.service.impl.UserTwoImpl

然后编写一个测试类

public class SpiTest {
    public static void main(String[] args) {
        ServiceLoader load = ServiceLoader.load(User.class);
        //使用了反射机制,创建了对象。
        Iterator iterator = load.iterator();
        while (iterator.hasNext()){
            User userImpl = iterator.next();
            userImpl.service();

        }
    }
}

控制台输出了 :实现类一   实现类二

原生SPI源码分析原理,SerivceLoader进入这个类,可以看到成员变量写好了路径,然后调用load方法

private static final String PREFIX = "META-INF/services/";

进入load.iterator(),重写了hasNext方法

 public Iterator iterator() {
        return new Iterator() {

            Iterator> 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方法

public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction action = new PrivilegedAction() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

进入hasNextService方法

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

 存到pending中,pending赋值给了nextName。

接下来回到iterator()方法中,继续走next方法,进入lookupIterator中的next方法

  public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction action = new PrivilegedAction() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

然后进入nextService()方法中。把之前的nextName值付给了cn,cn通过反射,取消类名c,类c 被实例化newInstance(),赋值给了p,然后返回p。p就是实例化对象,然后我们用接口进行接收的。这个就是Java原生的SPI的一个源码分析。

        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            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
        }

这就是java的原生SPI。有一些缺点:

接口的所有实现类全部都需要加载并实例化

无法根据参数来获取所对应的实现类

不能解决IOC、AOP的问题。基于以上问题,dubbo在原生SPI机制进行了增强,解决以上问题。

Dubbo的SPI机制,Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在META-INF/dubbo 路径下,配置内容如下,源码

private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
test1=com.jd.service.impl.UserOneImpl
test2=com.jd.service.impl.UserTwoImpl

先引入坐标依赖,



    com.alibaba
    dubbo
    2.5.3

在接口上加一个注解@SPI

重新编写一个测试类

public class DubboSpi {
    public static void main(String[] args) {
        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(User.class);
        User userImpl = extensionLoader.getExtension("test1");
        userImpl.service();
    }
}

控制台输出 ,实现类一,因为只配了key值是test1

源码分析,首先进入的是getExtensionLoader方法,通过 ExtensionLoader 的 getExtensionLoader 方法获取一ExtensionLoader 实例,该方法方法先从缓存中获取与拓展类对应的 ExtensionLoader,若缓存未命中,则创建一个新的实例

else {

            //从缓存中获取,如果缓存中有直接返回loader,如果缓存中没有,重新创建一个loader对象
            ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            if(loader == null) {
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            }

            return loader;

接下来看getExtension方法

public T getExtension(String name) {
		if (name == null || name.length() == 0)
		    throw new IllegalArgumentException("Extension name == null");
		if ("true".equals(name)) {
		    return getDefaultExtension();
		}
        //先从缓存中取,如果缓存中没有就新建一个holder对象
		Holder holder = cachedInstances.get(name);
		if (holder == null) {
		    cachedInstances.putIfAbsent(name, new Holder());
		    holder = cachedInstances.get(name);
		}
        //然后调用holder中的get方法,创建instance对象,如果是新建holder对象,get出来的是空的,如果是从缓存中取的get出来的不是空的。
		Object instance = holder.get();
        //如果是空的,使用createExtension创建实例,instance代表的就是我们那个接口的实例对象。
		if (instance == null) {
		    synchronized (holder) {
	            instance = holder.get();
	            if (instance == null) {
	                instance = createExtension(name);
	                holder.set(instance);
	            }
	        }
		}
		return (T) instance;
	} 
  

接下来进入createExtension方法中

 private T createExtension(String name) {
        //1、根据name值就是key值,通过getExtensionClasses()获取所有的拓展类,如果等于null就报异常
        Class clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //也是,从缓存中取拿,如果缓存没有,就创建一个
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
            //2、通过反射创建拓展对象实例
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //3、需要依赖注入,向实例中注入依赖
           //此处injectExtension方法依赖注入实现原理:Dubbo 首先会通过反射获取到实例的所 
           //  有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 
           //ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象 
           // 中。
            injectExtension(instance);
            
            Set> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                //4、将拓展对象包裹在相应的 Wrapper 对象中循环创建 Wrapper 实例
                for (Class wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //最后返回的实例就是,我们用接口接收的那个实例对象。
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

 

Dubbo的服务暴露

 

Dubbo的服务引入

你可能感兴趣的:(Dubbo)