Dubbo源码分析之SPI(三) | ExtensionLoader

mac2024-03-18  27

目录

一、概述

二、AdaptiveClassCodeGenerator

三、getExtension方法

四、getActivateExtension方法


 


一、概述

上一篇文章介绍了Dubbo的SPI机制ExtensionLoader源码,分析了ExtensionLoader如何从META-INFO/dubbo等目录下 获取service provider,并了解了它的缓存机制,缓存class字节码,缓存实例化异常Exception,缓存AdaptiveExtension实体对象, 以及它的自动注入ExtensionFactory的实现原理。

https://blog.csdn.net/qq_33513250/article/details/102818893

本文接着介绍ExtensionLoader通过JavassistCompiler动态生成AdaptiveExtension代码逻辑,并通过Protocol的加载进行实例分析和学习Dubbo的自动包装Wapper功能。

二、AdaptiveClassCodeGenerator

createAdaptiveExtensionClass方法中使用AdaptiveClassCodeGenerator动态生成代码,下面是生成的Protocol$Adaptive代码。

package org.apache.dubbo.rpc; import org.apache.dubbo.common.extension.ExtensionLoader; public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException( "The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException( "The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException( "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.refer(arg0, arg1); } public org.apache.dubbo.rpc.Exporter export(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.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException( "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.export(arg0); } }

其中destroy和getDefaultPort方法都是不支持的,会抛出异常UnsupportedOperationException。是因为接口上的Method,没有标注@Adaptive注解,只有标注@Adaptive注解的才会generateExtensionAssignment。

private String generateMethodContent(Method method) { Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); if (adaptiveAnnotation == null) { return generateUnsupported(method); } else { 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 code.append(generateUrlAssignmentIndirectly(method)); } String[] value = getMethodAdaptiveValue(adaptiveAnnotation); boolean hasInvocation = hasInvocationArgument(method); code.append(generateInvocationArgumentNullCheck(method)); code.append(generateExtNameAssignment(value, hasInvocation)); // check extName == null? code.append(generateExtNameNullCheck(value)); code.append(generateExtensionAssignment()); // return statement code.append(generateReturnAndInvocation(method)); } return code.toString(); }

  refer和export方法在这里可以看到生成code的时候,会根据他们的协议去自动使用不同的协议处理器处理逻辑。

a)when the url is registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=dubbo-sample, then the protocol is RegistryProtocol

b)when the url is dubbo://224.5.6.7:1234/org.apache.dubbo.config.api.DemoService?application=dubbo-sample, thenthe protocol is DubboProtocol

c) Actually,when the {@link ExtensionLoader} init the {@link Protocol} instants,it will automatically wraps two layers, and eventually will get a ProtocolFilterWrapper or ProtocolListenerWrapper

private String generateExtNameAssignment(String[] value, boolean hasInvocation) { // TODO: refactor it String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if (i == value.length - 1) { if (null != defaultExtName) { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); } } else { getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\")", value[i]); } } else { getNameCode = "url.getProtocol()"; } } } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); } } else { getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); }

三、getExtension方法

 ExtensionLoader还提供了通过name获取service provider的getExtension方法,为了保证线程安全采用了Holder对象持有name的实体类,具体创建逻辑在createExtension中。

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; }

 createExtension方法,会去获取所有的Class,同样先从缓存EXTENSION_INSTANCES中获取实体对象,然后再实例化,如果此name的接口Type类型有包装类加载,会对当前实体进行包装。如DubboProtocol实际上会被包装3层(ProtocolListenerWrapper,ProtocolFilterWrapper,QosProtocolWrapper)。

private T createExtension(String name) { 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); } injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }

四、getActivateExtension方法

Extensionloader还提供getActivateExtension方法,用来根据url的参数值选着不同的service provider。key,group参数分别为实现类上注解@Activate的value,grop值。

public List<T> getActivateExtension(URL url, String[] values) { return getActivateExtension(url, values, null); } public List<T> getActivateExtension(URL url, String key, String group) { String value = url.getParameter(key); return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group); }

上面两个方法实际调用如下方法,cachedActivates为Extensionloader为@Activate注解的缓存map,key为服务提供者名称,value为Activate对象。然后再遍历Activate对象,找到与传入参数匹配的name,然后遍历再调用getExtension(name)获取所有服务提供者。

public List<T> getActivateExtension(URL url, String[] values, String group) { List<T> exts = new ArrayList<>(); List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values); if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) { activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group(); activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value(); } else { continue; } if (isMatchGroup(group, activateGroup)) { T ext = getExtension(name); if (!names.contains(name) && !names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url)) { exts.add(ext); } } } exts.sort(ActivateComparator.COMPARATOR); } List<T> usrs = new ArrayList<>(); for (int i = 0; i < names.size(); i++) { String name = names.get(i); if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) { if (DEFAULT_KEY.equals(name)) { if (!usrs.isEmpty()) { exts.addAll(0, usrs); usrs.clear(); } } else { T ext = getExtension(name); usrs.add(ext); } } } if (!usrs.isEmpty()) { exts.addAll(usrs); } return exts; }

@Activate注解的缓存

private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();

cachedActivates缓存方法,同时兼容alibaba包下旧的注解

private void cacheActivateClass(Class<?> clazz, String name) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(name, activate); } else { // support com.alibaba.dubbo.common.extension.Activate com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class); if (oldActivate != null) { cachedActivates.put(name, oldActivate); } } }

 

最新回复(0)