2019独角兽企业重金招聘Python工程师标准>>>
本文重点讲述SPI机制,从jdk和dubbo
1、jdk spi机制
2、dubbo spi实现
首先spi是什么?
SPI是为某个接口寻找服务实现的机制。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
其次java spi是怎么找到实现类的?
java spi和所有实现接口的厂商有一个俗称的约定,只要将META-INF/services文件夹下生成一个和抽象类全名称(路径+类名称)相同的配置文件,那么厂商的jar包只要在工程路径下就能找到实现类。这种方式主要是解决不同厂商不同实现类的加载问题。在不修改java文件的情况下,如何才能够定位到不同的实现类。
JDK的spi机制有一个缺点,就是如果多个厂商的spi实现的jar包都在路径下,那么就要加载所有的实现类,这样很浪费资源。dubbo中每一种接口都有很多种实现,如果使用jdk这种方式自然做不到优雅的根据一个接口来获得该接口的实现。dubbo的目标就是:根据你配置的name获取某一个特定的接口实现,没有用到的其他接口实现就不能被实例化,免得浪费。因此,dubbo就按照SPI机制的原理自己实现了一套扩展机制。为实现dubbo的扩展机制,dubbo的SPI做到了一下三个方面。
1 可以方便的获取某一个想要的扩展实现,java的SPI机制就没有提供这样的功能
2 对于扩展实现IOC 依赖注入功能
3 对扩展采用装饰器模式进行功能增强,类似AOP实现的功能
dubbo的SPI实现具体如下。
首先定义一个SPI注解类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* 缺省扩展点名。
*/
String value() default "";
}
dubbo里面很多的接口都打了SPI注解,这些注解的实现要从一下三个文件夹下去寻找实现。
META-INF/dubbo/internal/ //dubbo内部实现的各种扩展都放在了这个目录了
META-INF/dubbo
/META-INF/services/
文件里面的内容全部都是key-value的形式存在。dubbo的各种接口有很多类型的实现。拿Protocol举例,它的实现:DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol等等。dubbo的扩展机制如何去查找你的实现类,并实现调用的呢?
ExtensionLoader<T>
拿ServiceConfig<T>中的这两个变量举例子。
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
这两个变量最终生成的既不是接口也不是具体的实现类,而是一个接口适配器类。这个适配器类动态的生成的,如下所示的代码:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
//根据url的信息去获取真实的实现类
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
if (arg2 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}
首先,我们来看适配器类是如何生成
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
第一步:通过proxyFactory.class类型生成一个ExtensionLoader<T>实例。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {每个定义的spi的接口都会构建一个ExtensionLoader实例,存储在EXTENSION_LOADERS
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;}
第二步:创建Adaptive实例,放入数组cachedAdaptiveInstance中。
public T getAdaptiveExtension() {Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {//创建Adaptive实例,放入数组cachedAdaptiveInstance中。instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);}}}}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);}}
return (T) instance;
}private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);}
}private Class<?> createAdaptiveExtensionClass() {
//生成类适配器,String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//编译这个类适配器为class文件
return compiler.compile(code, classLoader);
}
// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {//先读取SPI注解的value值,有值作为默认扩展实现的key
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()+ ": " + Arrays.toString(names));}
if(names.length == 1) cachedDefaultName = names[0];}}Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();//读取三个文件夹下的文件,将key-class放置到extensionClasses中loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);loadFile(extensionClasses, DUBBO_DIRECTORY);loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
//获取文件路径 目录+type名称String fileName = dir + type.getName();try {Enumeration<java.net.URL> urls;//类加载器ClassLoader classLoader = findClassLoader();if (classLoader != null) {urls = classLoader.getResources(fileName);} else {urls = ClassLoader.getSystemResources(fileName);}if (urls != null) {while (urls.hasMoreElements()) {java.net.URL url = urls.nextElement();try {BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));try {String line = null;while ((line = reader.readLine()) != null) {final int ci = line.indexOf('#');if (ci >= 0) line = line.substring(0, ci);line = line.trim();if (line.length() > 0) {try {String name = null;int i = line.indexOf('=');if (i > 0) {name = line.substring(0, i).trim();line = line.substring(i + 1).trim();}if (line.length() > 0) {
//读取到类Class<?> clazz = Class.forName(line, true, classLoader);
//判断实现类是否实现了type接口if (! type.isAssignableFrom(clazz)) {throw new IllegalStateException("Error when load extension class(interface: " +type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");}
//类中是否有方法打了Adaptive注解if (clazz.isAnnotationPresent(Adaptive.class)) {
//将打了Adaptive注解的类放置到cachedAdaptiveClass 中去。if(cachedAdaptiveClass == null) {cachedAdaptiveClass = clazz;}else if (! cachedAdaptiveClass.equals(clazz)) {throw new IllegalStateException("More than 1 adaptive class found: "+ cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName());}} else {//判断该是否有实现类是否存在入参为接口的构造器clazz.getConstructor(type);Set<Class<?>> wrappers = cachedWrapperClasses;if (wrappers == null) {cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();wrappers = cachedWrapperClasses;}//按照装饰器的角色将该类加进来wrappers.add(clazz);} // end of while urls}} catch (Throwable t) {logger.error("Exception when load extension class(interface: " +type + ", description file: " + fileName + ").", t);}
}
适配器中发现这个实现类也是由这个方法生成extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName)
//创建扩展类
instance = createExtension(name);
private T createExtension(String name) {
//加载类Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(name);}try {//从容器中获取是否存在T instance = (T) EXTENSION_INSTANCES.get(clazz);if (instance == null) {//容器中不存在,则按照类-实例对的形式放置到容器中EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());//获取该实例数据对instance = (T) EXTENSION_INSTANCES.get(clazz);}//set注入参数injectExtension(instance);//获取包装器类。class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapperSet<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && wrapperClasses.size() > 0) {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的SPI扩展机制,使得我们给出url就能动态的去获取真实的实现类。获得到真实的实现类,是实现功能的第一步。下面我们来看看spring如何加载到dubbo解析器类,并将dubbo功能收入囊下的。未完待续。
参考文献
http://m.blog.csdn.net/blog/u010311445/41577235