Dubbo
中可以通过SPI
机制来加载对应扩展点实现,可是一个扩展点对应着多个实现类,我们如何才能触发目标实现类中的方法呢?Dubbo
的Adaptive
机制就是解决这个问题的。
下面我们用代码来看一下@Adaptive注解的使用方式以及源码实现。
- 扩展点接口
ICarService
package com.dubbo.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface ICarService {
void speed(URL url);
}
- 两个扩展点实现类
AudiService
、BMWService
package com.dubbo.service.impl;
import com.dubbo.service.ICarService;
import org.apache.dubbo.common.URL;
// 实现类 奥迪
public class AudiService implements ICarService {
@Override
public void speed(URL url) {
System.out.println("audi drive speed 100KM/H");
}
}
// 实现类 宝马
public class BMWService implements ICarService {
@Override
public void speed(URL url) {
System.out.println("bmw drive speed 200KM/H");
}
}
- 扩展点配置文件
META-INF/services/com.lbb.service.ICarService
audi=com.lbb.service.impl.AudiService
bmw=com.lbb.service.impl.BMWService
一、类上添加@Adaptive
注解
我们可以在类上添加@Adaptive
注解来标示该类为当前扩展点的代理类,在代理类的方法中会根据传入参数从而自行选择实现类来进行调用。下面我们用具体的代码来实现一个代理类:
- 首先创建一个Adaptive类,集成扩展点接口
package com.lbb.adaptive;
import com.lbb.service.ICarService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;
@Adaptive
public class CarServiceAdaptive implements ICarService {
@Override
public void speed(URL url) {
// 从url参数中获取carType
String carType = url.getParameter("carType");
// 根据carType去获取对应的扩展点实现类
ICarService carService = ExtensionLoader.getExtensionLoader(ICarService.class).getExtension(carType);
// 调用扩展点实现类的实例的speed方法
carService.speed(url);
}
}
2. 并将其添加到扩展点文件中 ```txt audi=com.lbb.service.impl.AudiService bmw=com.lbb.service.impl.BMWService
adaptive=com.lbb.adaptive.CarServiceAdaptive # 此处添加的是适配代理类
<br/>
3. 下面是执行方法
```java
public static void main(String[] args) {
// 获取自适应方法
ICarService carService = ExtensionLoader.getExtensionLoader(ICarService.class).getAdaptiveExtension();
Map<String, String> map = new HashMap<>();
map.put("carType", "bmw");
URL url = new URL("", "", 0, map);
carService.speed(url);
}
// 输出结果
bmw drive speed 200KM/H
二、方法上添加@Adaptive
注解
我们也可以在扩展点接口的方法上添加@Adaptive
注解,从而让Dubbo为我们自动生成代理类,在代理类中也是会获取url参数,从而自行选择实现类来进行调用。下面我们用具体的代码来实现一下:
- 我们在
ICarService
类的speed()
方法上添加@Adaptive
注解,并在其参数中设置url参数对应的key
package com.lbb.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface ICarService {
@Adaptive("carType")
void speed(URL url);
}
- 执行方法同上,打印内容如下所示:
bmw drive speed 200KM/H
三、源码解析
为什么在方法上添加@Adaptive注解就能够自动为我们找到目标实现类呢?
从我们自己实现代理类中可知,Dubbo应该后台为我们生成了一个代理类。在这个代理类中会解析url中携带的参数,从而获取到目标实现类的别名,从而通过SPI机制获取对应的实例对象,再调用实例对象的方法。
- 这里我们便从
main
方法中的getAdaptiveExtenion()
方法为入口,探究Dubbo是如何实现的
// 每个接口都对应一个扩展加载器
ICarService carService = ExtensionLoader.getExtensionLoader(ICarService.class).getAdaptiveExtension();
- 这里会尝试着先从缓存中获取代理类对象实例,如果没有获取到再触发创建
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
public T getAdaptiveExtension() {
// 从缓存中获取扩展点对应的代理类对象实例
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
// 双重锁机制
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建代理类对象实例 (下面会详说)
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;
}
- 创建代理类、进行实例化、依赖注入
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension
private T createAdaptiveExtension() {
try {
// 注入依赖对象
return injectExtension((T)
// 获取代理类(下面会详说)
getAdaptiveExtensionClass()
// 进行实例化
.newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
- 这里会去检测是否存在类上配置了
@Adaptive
注解的代理类,如果存在则直接返回该代理类,如果不存在则创建一个代理类org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 加载扩展点实现类
getExtensionClasses();
// 如果存在在类上配置@Adaptive的代理类,则直接返回该代理类
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 如果不存在,则创建一个代理类(下面会详说)
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
- 生成代理类的代码,并进行编译,我们等会将这个代理类的代码打印出来看看,看看到底有什么玄机在里面
private Class<?> createAdaptiveExtensionClass() {
// 生成代理类的字符串
String code = createAdaptiveExtensionClassCode();
// 这里我们打印出来看看这个代理类的代码
System.out.println(code);
// 下面是对这个代理类字符串进行编译,生成代理类Class对象
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
- 打印出的代理类代码
package com.lbb.service;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ICarService$Adaptive implements com.lbb.service.ICarService {
public void speed(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("carType");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.lbb.service.ICarService) name from url(" + url.toString() + ") use keys([carType])");
com.lbb.service.ICarService extension = (com.lbb.service.ICarService)ExtensionLoader.getExtensionLoader(com.lbb.service.ICarService.class).getExtension(extName);
extension.speed(arg0);
}
}