Dubbo SPI
在前面的学习中ServiceConfig中有一段
private static final Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
点击进入Protocol ,可以看到一个特殊的注解@SPI。学习一下:
java spi机制
SPI,Service Provider Interface,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,mysql和postgresql都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。
spi 经典的思想体现,大家平时都在用,比如说 jdbc。
Java 定义了一套 jdbc 的接口,但是 Java 并没有提供 jdbc 的实现类。
但是实际上项目跑的时候,要使用 jdbc 接口的哪些实现类呢?一般来说,我们要根据自己使用的数据库,比如 mysql,你就将 mysql-jdbc-connector.jar 引入进来;oracle,你就将 oracle-jdbc-connector.jar 引入进来。
在系统跑的时候,碰到你使用 jdbc 的接口,他会在底层使用你引入的那个 jar 中提供的实现类。
回到上面的问题:
Protocol 接口,在系统运行的时候,,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。
它会去找一个你配置的 Protocol,将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了。
spi机制有几个重要的注解:
1、@SPI注解,被此注解标记的接口,就表示是一个可扩展的接口,并标注默认值。
2、@Adaptive注解,有两种注解方式:一种是注解在类上,一种是注解在方法上。
3、@Activate注解,此注解需要注解在类上或者方法上,并注明被激活的条件,以及所有的被激活实现类中的排序信息
接下来继续看代码,可以看到Protocol的spi的默认值是dubbo,这个在初始化类的时候很有用,假如你的Protocol实现类有很多,在dubbo中就有如下实现类:
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> var1) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;
void destroy();
}
@Adaptive注解:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) //只能注解在类、接口、方法上面
public @interface Adaptive {
String[] value() default {};
}
@adaptive注解在类上面:AdaptiveExtensionFactory @adaptive注解在方法上面:Protocol
注解在类上:代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),例如:ExtensionFactory
注解在方法上:代表自动生成和编译一个动态的adpative类,例如:Protocol$adpative
dubbo对于SPI的实现主要是在ExtensionLoader这个类中,这个类主要有三个方法:
getExtension():主要用于获取名称为name的对应的子类的对象,这里如果子类对象如果有AOP相关的配置,这里也会对其进行封装;
getAdaptiveExtension():使用定义的装饰类来封装目标子类,具体使用哪个子类可以在定义的装饰类中通过一定的条件进行配置;
getExtensionLoader():加载当前接口的子类并且实例化一个ExtensionLoader对象。
public T getExtension(String name);
public T getAdaptiveExtension();
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type);
具体学习内容可以参考:https://www.jianshu.com/p/0ee6e2455e02
-----------------------getAdaptiveExtension()
-->getAdaptiveExtension() //目的为 cachedAdaptiveInstance赋值
-->createAdaptiveExtension()
-->getAdaptiveExtension()
-->getExtensionClasses() //目的为cachedClasses赋值
-->loadExtensionClasses() //加载
-->loadFile() //加载配置信息(主要是META_INF/services/下)
-->createAdaptiveExtensionClass() //自动生成和编译一个动态的adpative类,这个类是个代理类
-->createAdaptiveExtensionClassCode()//通过adaptive模板生成代码
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//编译
-->compile(String code, ClassLoader classLoader)
-->injectExtension()//作用:进入IOC的反转控制模式,实现了动态入注
对我来说这些还有点难。。未完待续。。