简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射。java
(此图来自网上,我没有刻意去截图)ide
而后在这个文件里面写入实现类函数
com.blueskykong.javaspi.serializer.KryoSerializerurl
com.blueskykong.javaspi.serializer.JavaSerializerspa
可是dubbo的SPI格式变了,也就意味着不能直接使用java SPI了code
文件的目录类似,可是里面的内容变为了key-valueblog
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory接口
结论:所谓的这些SPI只不过是一种方式而已,最后都是反射生成类(还有classloader的问题,这里咱们不讨论),组织方式能够随便变,可是既然是分析dubbo,那么这里就看dubbo是怎么最终实现的ssl
dubbo至关于使用了key-value的形式,用key来映射每一个类,这样用户就能够在url加入这个key来实现动态加载某个类的需求,字符串
那么为何java SPI的方式不行:
如今使用key-value的方式已经能够由用户指定key去加载class,可是出于系统的考虑还有一些功能须要扩展
dubbo中负责SPI的处理的类主要是 ExtensionLoader
这里我标注出主要的变量含义
/** * 这个三个表明须要从哪一个resource路径下加载咱们的key-value文件 */ private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/"; /** * 在注解里面的value是可使用逗号(,)来设置多个值 */ private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); /** * ExtensionLoader 是每一个接口对应一个 这个map保存了接口和对应的ExtensionLoader的对应 */ private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); /** * 保存了配置文件中全部value-key之间的映射 */ private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); // 以上都是静态变量也就是是全局的 /** * 当前这个ExtensionLoader 对应的那个接口 */ private final Class<?> type; private final ExtensionFactory objectFactory; /** * value-key之间的反映射 */ private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); /** * 配置文件中key-value之间的映射 value指的是类class */ private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>(); /** * 这里保存着这个接口的实现类含有@Activate注解 */ private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); /** * 这个接口下 key-value的映射 value指的是实例 */ private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); /** * 适配类的实例 */ private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); /*** * 这个接口的适配类 也就是注解@Adaptive的注解加载类的这个类就是适配类 所谓的适配类 就是实现根据url里面的参数 * 来决定使用这个接口的哪一个子类 */ private volatile Class<?> cachedAdaptiveClass = null; /** * SPI注解里面value 做为这个接口的默认加载类 */ private String cachedDefaultName;
总结一下:
在key-value映射中,会分为包装类和正常类,所谓的包装类就是利用装饰器模式,来实现对原功能进行一些额外的处理,识别方式是按照是否有一个构造方法含有这个接口做为参数为标识
这里的注入是指对接口里面的变量进行注入实例
还在更新......