一、spi 使用
创建spi 接口及对应实现
package apache.dubbo.test.spi;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface SpiInterface {
void method();
}
public class SpiInterfaceImpl implements SpiInterface {
@Override
public void method() {
System.out.println(this.getClass().getName());
}
}
在META-INFO/dubbo中声明审批配置,创建与spi接口类名相同的文件apache.dubbo.test.spi.SpiInterface,在文档内编辑
my=apache.dubbo.test.spi.SpiInterfaceImpl
使用 SPI
ExtensionLoader<SpiInterface> extensionLoader = ExtensionLoader.getExtensionLoader(SpiInterface.class);
SpiInterface spiInstance = extensionLoader.getExtension("my");
spiInstance.method();
二、原理解析
public class ExtensionLoader<T> {
//loader 缓存
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// ...
return (ExtensionLoader<T>) EXTENSION_LOADERS.computeIfAbsent(type, k -> new ExtensionLoader<T>(type));
}
// 获取实例
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 注解中配置的默认实现
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从缓存获取
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//创建实例
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
// 创建实例
private T createExtension(String name) {
//获取class对象
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//实例化
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//注入set方法
injectExtension(instance);
//包装类
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
//如果有包装类,对对象进行包装
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
//执行init
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
// 获取审批配置
/**
* 获取全量的class配置
* @return
*/
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 解析META-INFO下的配置文件
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
/**
* synchronized in getExtensionClasses
* */
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 从对应文件路径下加载文件 [META-INF/services/,META-INF/dubbo/,internal/]
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.excludedPackages());
}
return extensionClasses;
}
}
三、Adaptive
Dubbo Spi 提供获取适配对象的代理适配器的api,dubbo中大量使用了该方法,实现动态的调用
ExtensionLoader<SpiInterface> extensionLoader = ExtensionLoader.getExtensionLoader(SpiInterface.class);
SpiInterface spiInstance = extensionLoader.getAdaptiveExtension();
动态适配器对象有两种创建方式
1 Adaptive 类,用户自定义
用户创建一个类继承SPI接口,添加@Adaptive注解,用户自己实现对象的配逻辑
@Adaptive
public class SpiIoc implements SpiInterface {
@Override
public void method(URL url) {
// 适配逻辑
}
}
2 Adaptive方法,dubbo自动创建
SPI接口方法被 @Adaptive标记,参数列表总必须带有org.apache.dubbo.common.URL,或者参数对象里有对象包含获取URL对象的get方法,针对这些方法,dubbo会为其创建代理方法
@SPI
public interface SpiInterface {
@Adaptive("my") // 如果注解value是protocol,通过url的协议名称进行适配;否则通过url同名的parameter适配
void method(URL url);
}
源码实现
public class ExtensionLoader<T> {
// ...
//nb 该方法是实际生成的是一个代理适配器,根据spi 类型去调用对应的类
public T getAdaptiveExtension() {
// 缓存
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
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);
}
}
}
}
return (T) instance;
}
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();
// 有Adaptive类 在之前加载SPI配置文件时已经初始化
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 通过javassist自动创建代理类
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);
}
}
下面是代码生成器的源码
public class AdaptiveClassCodeGenerator {
/**
* 生成代码
* generate and return class code
*/
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
// 必须带有@Adaptive的方法
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
code.append(generatePackageInfo());
code.append(generateImports());
code.append(generateClassDeclaration());
// 组装代理方法
Method[] methods = type.getMethods();
for (Method method : methods) {
code.append(generateMethod(method));
}
code.append("}");
if (logger.isDebugEnabled()) {
logger.debug(code.toString());
}
return code.toString();
}
/**
* 创建代理方法的代码
* generate method declaration
*/
private String generateMethod(Method method) {
String methodReturnType = method.getReturnType().getCanonicalName();
String methodName = method.getName();
// 方法内容
String methodContent = generateMethodContent(method);
String methodArgs = generateMethodArguments(method);
String methodThrows = generateMethodThrows(method);
return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}
/**
* 生成代理方法
* generate method content
*/
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
// 非Adaptive方法,代理方法直接抛异常
if (adaptiveAnnotation == null) {
return generateUnsupported(method);
} else {
// 查找方法参数里的 URL
int urlTypeIndex = getUrlTypeIndex(method);
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
// did not find parameter in URL type
// 查找参数里的对象包含的 URL
code.append(generateUrlAssignmentIndirectly(method));
}
//方法中@Adaptive 的value
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
boolean hasInvocation = hasInvocationArgument(method);
// 组装org.apache.dubbo.rpc.Invocation 参数中指定的method
code.append(generateInvocationArgumentNullCheck(method));
// 组装要代理的Extension的ExtName,看是去url的parameter还是Protocol
// value=protocol代表取url的protocol名称,其他值代表取url中相对应的parameter的值
code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null?
code.append(generateExtNameNullCheck(value));
// 组装通过SPI获取对应的Extension
code.append(generateExtensionAssignment());
// return statement
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
// 没有从SPI接口方法里找到参数URL,遍历参数,从参数的下级去找URL
private String generateUrlAssignmentIndirectly(Method method) {
Class<?>[] pts = method.getParameterTypes();
Map<String, Integer> getterReturnUrl = new HashMap<>();
// find URL getter method
for (int i = 0; i < pts.length; ++i) {
for (Method m : pts[i].getMethods()) {
String name = m.getName();
if ((name.startsWith("get") || name.length() > 3)
&& Modifier.isPublic(m.getModifiers())
&& !Modifier.isStatic(m.getModifiers())
&& m.getParameterTypes().length == 0
&& m.getReturnType() == URL.class) {
getterReturnUrl.put(name, i);
}
}
}
if (getterReturnUrl.size() <= 0) {
// getter method not found, throw
throw new IllegalStateException("Failed to create adaptive class for interface " + type.getName()
+ ": not found url parameter or url attribute in parameters of method " + method.getName());
}
Integer index = getterReturnUrl.get("getUrl");
if (index != null) {
return generateGetUrlNullCheck(index, pts[index], "getUrl");
} else {
Map.Entry<String, Integer> entry = getterReturnUrl.entrySet().iterator().next();
return generateGetUrlNullCheck(entry.getValue(), pts[entry.getValue()], entry.getKey());
}
}
}
最终dubbo找到生成的代码如下,实际是返回了一个代理对象,通过接口参数里的URL中的信息对SPI进行适配
package apache.dubbo.test.spi;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class SpiInterface$Adaptive2 implements apache.dubbo.test.spi.SpiInterface {
public void method(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("my");
if(extName == null) throw new IllegalStateException("Failed to get extension (apache.dubbo.test.spi.SpiInterface) name from url (" + url.toString() + ") use keys([my])");
apache.dubbo.test.spi.SpiInterface extension = (apache.dubbo.test.spi.SpiInterface)ExtensionLoader.getExtensionLoader(apache.dubbo.test.spi.SpiInterface.class).getExtension(extName);
extension.method(arg0);
}
}
具体适配逻辑由于接口参数不同或者注解Adaptive的value不同实现稍有区别,但核心都是通过url的参数或者协议指定extName,在通过SPI返回对应的Extension
四、自动包装
之前提到在创建实例时会有一个自动包装的过程
// 创建实例
private T createExtension(String name) {
//获取class对象
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//实例化
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//注入set方法
injectExtension(instance);
//包装类
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
//如果有包装类,对对象进行包装
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
//执行init
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
如果有包装,是返回一个包装类的实例
包装类的判断通过检查SPI实现类是否包含参数是该SPI接口的构造函数,逻辑如下
private void cacheWrapperClass(Class<?> clazz) {
if (cachedWrapperClasses == null) {
cachedWrapperClasses = new ConcurrentHashSet<>();
}
cachedWrapperClasses.add(clazz);
}
/**
* test if clazz is a wrapper class
* <p>
* which has Constructor with given class type as its only argument
*/
private boolean isWrapperClass(Class<?> clazz) {
try {
//判断条件 带有参数的public构造器 且 构造器入参是需要包装的SPI接口
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
使用可以使用该功能实现AOP的操作
实现包装逻辑
public class SpiInterfaceWrapper implements SpiInterface {
private SpiInterface spiInterface;
public SpiInterfaceWrapper(SpiInterface spiInterface) {
this.spiInterface = spiInterface;
}
@Override
public void method(URL url) {
System.out.println("wrapper do");
spiInterface.method(url);
System.out.println("wrapper after");
}
}
在META-INFO配置中添加该SPI实现
my=apache.dubbo.test.spi.SpiInterfaceImpl
spiIoc=apache.dubbo.test.spi.SpiIoc
wrapper=apache.dubbo.test.spi.SpiInterfaceWrapper
这样最终获取到的SPI实例将会实现自动包装