前言
之前我们了解了整个class的加载过程,但是里面还有一些内容需要了解下,就是标记了adaptive的注解。
在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler
和 AdaptiveExtensionFactory
。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive 是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。Adaptive 注解的地方不同,相应的处理逻辑也是不同的。一般类上的比较简单,方法上的比较复杂,这里根据上次的SPI继续了解分析。
目的
dubbo通过SPI机制就能加载类,但是有些类不希望直接加载,而是系统在扩展方法加载的时候,进行加载。所以才需要Adaptive方式。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类
获取adaptive
下面是adaptive的源码。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
当加载Loader时,我们首先要构建它。这里会设置下factory,会拿到默认的getAdaptiveExtension
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
getAdaptiveExtension
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);
}
}
下面的create才是真正的方法,它包含了好几个逻辑。
- getAdaptiveExtensionClass()获取到这个class。
- 对这个class进行实例化。
- 进行扩展对象的注入。这里特别注入,我们的自适应扩展adapter有2种情况,一种是手动编码的,一种是自动生成的。手动编码的类可能需要我们注入其他的类依赖。
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
//加载该type所有的class
getExtensionClasses();
//上一步加载时,如果loadClass()方法里面有判断,如果class类上标记了Adaptive就会缓存进去
//clazz.isAnnotationPresent(Adaptive.class)
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//创建自适应类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
//使用class编译创建出来类
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);
}
我们知道当ExtensionFactory的 AdaptiveExtensionFactory
就有这个Adapter标记,所以当时new ``ExtensionLoader
时,就直接返回了此类。对于没有标记这个的,我们后面再看下这个自动生成。先看下注入。
injectExtension
这里dubbo的IOC体现了,拿到class后会进行注入操作。
private T injectExtension(T instance) {
//如果是factory,没必要注入了
if (objectFactory == null) {
return instance;
}
try {
// 遍历目标类的所有方法
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取属性名,比如 setName 方法对应属性名 name
String property = getSetterProperty(method);
//从dubbo总的factory加载容器中,找到这个class实例
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//调用方法注入
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
这里的理解就是,当我们写了一个类时,需要Loader加载时,这时候Loader加载的时候,会帮我们将这个class的setting方法都注入好实例。例如:
public class DubboBumbleBee implements DubboRobot {
private ExtensionFactory extensionFactory;
@Override
public void sayHello() {
System.out.println("dubbo spi amazing.");
}
public ExtensionFactory getExtensionFactory() {
return extensionFactory;
}
//这里当ExtensionLoader加载这个class时,发现方法含有set注入,然后就会再次查找加载extensionFactory
//循环加载到extensionLoader时,就会设置注入进去
public void setExtensionFactory(ExtensionFactory extensionFactory) {
this.extensionFactory = extensionFactory;
}
}
AdaptiveExtensionFactory
我们所有的dubbo相关的类,都是通过这个默认加载的。
//标记了Adaptive,手动编写的
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
//SpiExtensionFactory:dubbo自己的方式,通过spi获取到实例
//SpringExtensionFactory:设置spring的ApplicationContext,然后获取拿到信息
private final List<ExtensionFactory> factories;
//创建时,拿到所有的factory,并注入进去
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;
}
}
AdaptiveClassCodeGenerator
如果类上标记了Adaptive
注解,那么再getAdaptiveExtension()
时,将会直接返回。如果没有标记,是class的方法上标记的,这时候就需要这个generator进行处理生成了。
generate
总的生成方法
/**
* 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();
// 生成 package 代码:package + type 所在包
code.append(generatePackageInfo());
// 生成 import 代码:import + ExtensionLoader 全限定名
code.append(generateImports());
// 生成类代码:public class + type简单名称 + $Adaptive + implements + type全限定名 + {
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();
}
generateMethod
这里面主要的方法就是生产方法的主体
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);
}
generateMethodContent
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
//不存在,生成不支持的方法
return generateUnsupported(method);
} else {
//遍历method.getParameterTypes()获取参数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,那么继续通过method.getParameterTypes()获取所有的参数
// 找下哪个参数含有getUrl()方法
code.append(generateUrlAssignmentIndirectly(method));
}
//获取方法注解的Adaptive的值,如果没有获取类名,并将类名转换为字符数组
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
//是否含有Invocation参数
boolean hasInvocation = hasInvocationArgument(method);
//含有Invocation参数进行判空处理
code.append(generateInvocationArgumentNullCheck(method));
//开始从参数中获取真正的方法,获取路径有
//url.getParameter url.getMethodParameter url.getProtocol()
code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null? 判空处理
code.append(generateExtNameNullCheck(value));
// 生成Loder
code.append(generateExtensionAssignment());
// return statement
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
经过如上方法,便生成了代理class的文件,然后进行代理class的生成。
例子
这里以ProxyFactory
为例,自适应加载类。ProxyFactory
的源码如下:
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({"proxy"})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
@Adaptive({"proxy"})
<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
@Adaptive({"proxy"})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
结果代理类后,生成的代理类如下:
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0, arg1);
}
public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
if (arg2 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}
当使用这个方法时,我们才会从代理类中,进行加载类。由url.getParameter url.getMethodParameter url.getProtocol() 不同方法来获取处理。
参考
- 官方:SPI自适应扩展