参考:http://www.cnblogs.com/heart-king/p/5632524.html
SPI是什么
SPI是为某个接口寻找服务实现的机制。为实现在模块装配的时候不在程序里动态指明,这就需要一种服务发现机制。
原理
java spi和所有实现接口的厂商有一个俗称的约定,只要将META-INF/services文件夹下生成一个和抽象类全名称相同的配置文件,那么厂商的jar包只要在工程路径下就能找到实现类。
JDK的spi机制有一个缺点,就是如果多个厂商的spi实现的jar包都在路径下,那么就要加载所有的实现类,这样很浪费资源。dubbo的目标就是:根据你配置的name获取某一个特定的接口实现,没有用到的其他接口实现就不能被实例化。因此,dubbo就按照SPI机制的原理自己实现了一套扩展机制。dubbo的SPI做到了一下三个方面:
1.可以方便的获取某一个想要的扩展实现。
2.对于扩展实现IOC,依赖注入功能。
3.对扩展采用装饰器模式进行功能增强,类似AOP实现的功能。
dubbo的SPI实现
1.首先定义一个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/
我们以Protocol接口为例,接口上打上SPI注解,默认扩展点名为dubbo
@SPI("dubbo")
public interface Protocol{
}
ExtensionLoader
1,ExtensionLoader.getExtensionLoader(Protocol.class)
每个定义的spi的接口都会构建一个ExtensionLoader实例,存储在ConcurrentMap<Class<?>,ExtensionLoader<?>> EXTENSION_LOADERS这个map对象中。
2.LoadExtensionClasses读取扩展点中的实现类
a)先读取SPI注解的value值,有值作为默认扩展实现的key。
b)依次读取路径的文件
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
META-INF/services/com.alibaba.dubbo.rpc.Protocol
3.loadFile逐行读取com.alibaba.dubbo.rpc.Protocol文件中的内容,每行内容以key/value形式存储的。
a)判断类实现上有没有打上@Adaptive实现,如果打上了注解,将此类作为protocol协议的适配器缓存起来,读取下一行。
b)如果类实现没有打上@Adaptive,判断实现类是否存在入参接口的构造器,有的话作为包装类缓存到此ExtensionLoader的Set<Class<?>>集合中,这个其实是个装饰模式。
c)如果即不是适配对象也不是wrapped的对象,那就是扩展点的具体实现对象。查找实现类上有没有打上@Activate注解,有缓存到变量cacheActivates的map中。
4.获取或者创建适配对象getAdaptiveExtension
a)如果cachedAdaptiveClass有值,说明有且仅有一个实现类打了@Adaptive,实例化这个对象返回。
b)如果cachedAdaptiveClass为空,创建适配器字节码。
Dubbo采用统一数据模型com.alibaba.dubbo.common.URL(它是dubbo定义的数据模型不是jdk的类),URL中定义的协议字段protocol,会根据具体业务设置不同的协议。
适配器的作用是根据url.getProtocol()的值extName,去ExtensionLoader.getExtension(extName)选取具体的扩展点实现。
所以能够利用javasist生成适配器的条件:
(1)接口方法中必须至少有一个方法打上了@Adaptive注解。
(2)打上了@Adaptive注解的方法参数必须有URL类型参数或者参数中存在getURL方法。
5.通过createAdaptiveExtensionClassCode生成java源码代码,要被java虚拟机加载执行必须编译成字节码,dubbo提供两种方式执行代码的编译jdk和javassit
6.自动wrap上扩展wrap类
7.IOC
dubbo的ExtensionLoader在加载扩展实现的时候内部实现了简单的ioc机制来实现对扩展实现所依赖的参数注入,
最后欢迎大家访问我的个人网站:1024s