dubbo spi详解
dubbo的spi详解 前面大概讲了一下dubbo的spi机制.其中讲到注入的时候,在Car上加了个注解 @Adaptive(value = “carType”),讲到会通过url里面的参数,key为carType,根据value来获取对应的car.这里面就要讲到Adaptive 注解 - 自适应扩展;
自适应扩展
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。
-
自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。扩展的接口生成一个代理类,可以通过JDK 或者 javassist 编译你生成的代理类代码,然后通过反射创建实例。 这个生成的代理类代码里面的实现会根据本来方法的请求参数得知需要的扩展类,然后通过 ExtensionLoader.getExtensionLoader(type.class).getExtension(从参数得来的name),来获取真正的实例来调用。 -
当然也可以就是自己创建个adapter类,来手动实现自适应扩展.在类上加个@Adaptive注解就好了
说简单点就是我想根据配置来进行 SPI 扩展的加载,但是我不想在启动的时候让扩展被加载,我想根据请求时候的参数来动态选择对应的扩展。
demo
新建个Bus类,然后同Car类一样建两个实现,再建一个BusRouter类作为参数路由类,然后在创建一个BusAdapter类作为自适应类,并配置到配置文件中:
public class BusRouter {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@SPI
public interface Bus {
@Adaptive
void getColor(BusRouter busRouter);
}
public class BlackBus implements Bus {
@Override
public void getColor(BusRouter busRouter) {
System.out.println("black");
}
}
public class RedBus implements Bus {
@Override
public void getColor(BusRouter busRouter) {
System.out.println("red");
}
}
@Adaptive
public class BusAdapter implements Bus {
@Override
public void getColor(BusRouter busRouter) {
ExtensionLoader<Bus> extensionLoader =
ExtensionLoader.getExtensionLoader(Bus.class);
Bus red = extensionLoader.getExtension(busRouter.getName());
red.getColor(busRouter);
}
}
在META-INF.services下建立文件org.example.api:
black=org.example.impl.BlackBus
red=org.example.impl.RedBus
org.example.BusAdapter
测试类:
@Test
public void driverBus1() throws Exception {
ExtensionLoader<Bus> extensionLoader =
ExtensionLoader.getExtensionLoader(Bus.class);
Bus trucker = extensionLoader.getAdaptiveExtension();
BusRouter busRouter = new BusRouter();
busRouter.setName("red");
trucker.getColor(busRouter);
}
源码分析
在构建ExtensionLoader的时候,会初始化ExtensionFactory, getAdaptiveExtension方法会直接返回带有@Adaptive标注的类,而cachedAdaptiveInstance在创建ExtensionLoader的时候就加载了
@SuppressWarnings("unchecked")
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("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
有@Adaptive标注的类的实现
前面讲ExtensionFactory的时候就说了两个实现.实际是有三个实现; 实际初始化的是AdaptiveExtensionFactory这个类,AdaptiveExtensionFactory是少有被标注的类,还有一个是AdaptiveCompiler类.AdaptiveExtensionFactory类的源码比较简单,就是获取到对应实现,然后getExtension遍历获取对应的类型类,ExtensionFactory其实就dubbo spi的自适应的实现:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
如果没有@Adaptive标注的类
没有cachedAdaptiveClass类则会通过createAdaptiveExtension->getAdaptiveExtensionClass->createAdaptiveExtensionClass方法进行创建:
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
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);
}
比如前面的测试类bus,通过AdaptiveClassCodeGenerator生成的code如下:
package org.example.api;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Car$Adaptive implements org.example.api.Car {
public void getColor(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("Failed to get extension (org.example.api.Car) name from url (" + url.toString() + ") use keys([carType])");
org.example.api.Car extension = (org.example.api.Car) ExtensionLoader.getExtensionLoader(org.example.api.Car.class).getExtension(extName);
extension.getColor(arg0);
}
}
可以看到会生成包,也会生成 import 语句,类名就是接口加个$Adaptive,并且实现这接口,如果没有标记 Adaptive 注解的方法调用的话直接抛错。内部生成逻辑也比较简单就是通过url.getParameter(“carType”)方法来获取对应的扩展点名称,然后再通过ExtensionLoader.getExtensionLoader(org.example.api.Car.class).getExtension(extName);来获取对应的扩展类,执行对应方法. 生成类generate()方法感兴趣可以自己看下.
|