Java SPI 与 dubbo SPI (2)

RobotA

public class RobotA implements Robot { public RobotA() { System.out.println("Happy RobotA is loaded"); } @Override public void sayHello() { System.out.println("i am a very very happy Robot "); } public void sayBye(){} }

RobotB

public class RobotB implements Robot { public RobotB() { System.out.println("SB RobotB is loaded"); } @Override public void sayHello() { System.out.println("i am a da sha bi "); } public void sayBye(){} } 配置实现类与接口

在META-INF/dubbo目录下建立一个以接口全限定名为名字的文件,里面的内容是自定义名字与类的全限定名的键值对,举个例子

robotA = cn.testlove.double_dubbo.inter.impl.RobotA robotB=cn.testlove.double_dubbo.inter.impl.RobotB 原理

我们通过对下列代码的调用来进行分析

ExtensionLoader<Robot> extensionLoader= ExtensionLoader.getExtensionLoader(Robot.class); Robot robotB = extensionLoader.getExtension("robotB");

第一句代码没什么好说的,只是获取一个Robot的ExtensionLoader对象并且缓存在Map中,下次如果是同样的接口可以直接从map中获取

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); }

再来看第二句代码

//从缓存中找 final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); //双重检查 if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name); holder.set(instance); } } }

首先从缓存里找,找不到再创建一个新的对象。

再看createExtension(name)方法

Class<?> clazz = getExtensionClasses().get(name); T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } initExtension(instance); return instance;

注意对于Class<?> clazz = getExtensionClasses().get(name);这一句的理解,这一句是获取配置文件中所有类的Class实例,而不是获取所有扩展类的实例。

接下来的流程其实也就简单了从EXTENSION_INSTANCES缓存中获取instance实例,如果没有,就借助Class对象实例化一个,再放入缓存中

接着用这个instance去实例化一个包装类然后返回.自此,一个我们需要的对象产生了.

最后我们看看getExtensionClasses()这个方法

Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes;

这里的classes就是用来存各个扩展类Class的Map缓存,如果不存在的话,会调用loadExtensionClasses();去加载,剩下的就是找到对应路径下的配置文件,获取全限定名了

上文我在分析Dubbo SPI时,多次提到Map,缓存二词,我们可以具体有以下这些.其实看名字就大概知道作用了

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>() private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); private volatile Class<?> cachedAdaptiveClass = null; private String cachedDefaultName;

对比原生的java SPI,dubbo的无疑更灵活,可以按需去加载某个类,也可以很便捷的通过自定义的名字去获取类.而且Dubbo还支持setter注入.这点以后再讲.

最后提一个问题,java原生的SPI只有在用iterator遍历到的时候才会实例化对象,那能不能在遇到自己想要的实现对象时就停止遍历,避免不必要的资源消耗呢?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzspjd.html