前言
之前我们了解了整个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所有的classgetExtensionClasses();//上一步加载时,如果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 方法对应属性名 nameString 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;@Overridepublic 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,手动编写的@Adaptivepublic 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);}@Overridepublic <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()获取参数URLint urlTypeIndex = getUrlTypeIndex(method);// found parameter in URL typeif (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));// 生成Lodercode.append(generateExtensionAssignment());// return statementcode.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自适应扩展
