前言
前面我们已经分析Dubbo SPI相关的源码,看过的小伙伴相信已经知晓整个加载过程,我们也留下两个问题,今天我们先来处理下其中关于注解Adaptive的原理。
什么是@Adaptive
对应于Adaptive机制,Dubbo提供了一个注解@Adaptive,该注解可以用于接口的某个子类上,也可以用于接口方法上。如果用在接口的子类上,则表示Adaptive机制的实现会按照该子类的方式进行自定义实现;如果用在方法上,则表示Dubbo会为该接口自动生成一个子类,并且重写该方法,没有标注@Adaptive注解的方法将会默认抛出异常。对于第一种Adaptive的使用方式,Dubbo里只有ExtensionFactory接口使用,AdaptiveExtensionFactory的实现就使用了@Adaptive注解进行了标注,主要作用就是在获取目标对象时,分别通过ExtensionLoader和Spring容器两种方式获取,该类的实现已经在Dubbo SPI机制分析过,此篇文章关注的重点是关于@Adaptive注解修饰在接口方法的实现原理,也就是关于Dubbo SPI动态的加载扩展类能力如何实现,搞清楚Dubbo是如何在运行时动态的选择对应的扩展类来提供服务。简单一点说就是一个代理层,通过对应的参数返回对应的类的实现,运行时编译。为了更好的理解我们来写个案例:
@SPI("china")
public interface PersonService {
@Adaptive
String queryCountry(URL url);
}
public class ChinaPersonServiceImpl implements PersonService {
@Override
public String queryCountry(URL url) {
System.out.println("中国人");
return "中国人";
}
}
public class EnglandPersonServiceImpl implements PersonService{
@Override
public String queryCountry(URL url) {
System.out.println("英国人");
return "英国人";
}
}
public class Test {
public static void main(String[] args) {
URL url = URL.valueOf("dubbo://192.168.0.101:20880?person.service=china");
PersonService service = ExtensionLoader.getExtensionLoader(PersonService.class)
.getAdaptiveExtension();
service.queryCountry(url);
}
}
china=org.dubbo.spi.example.ChinaPersonServiceImpl
england=org.dubbo.spi.example.EnglandPersonServiceImpl
该案例中首先构造了一个URL对象,这个URL对象是Dubbo中进行参数传递所使用的一个基础类,在配置文件中配置的属性都会被封装到该对象中。这里我们需要注意的是我们的对象是通过一个url构造的,并且在url的最后有一个参数person.service=china,这里也就是我们所指定的使用哪种基础服务类的参数,通过指向不同的对象就可以生成对应不同的实现。关于URL部分的介绍我们在下一篇文章介绍,聊聊Dubbo中URL的使用场景有哪些。 在构造一个URL对象之后,通过getExtensionLoader(PersonService.class)方法获取了一个PersonService对应的ExtensionLoader对象,然后调用其getAdaptiveExtension()方法获取PersonService接口构造的子类实例,这里的子类实际上就是ExtensionLoader通过一定的规则为PersonService接口编写的子类代码,然后通过javassist或jdk编译加载这段代码,加载完成之后