dubbo系列之内核SPI-Protocol扩展类生成(九)
前文回顾
上一篇文章我们讲到,扩展实现类的类上如果写上了@Adaptive注解,则可以直接取得扩展实现类对象,其他的需要通过字节码生成技术来生成对象,接着上文的代码讲解,调用createAdaptiveExtensionClass方法生成扩展实现类对象
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 获取扩展实现类的代码code, 这个类里面是拼接的字符穿
String code = createAdaptiveExtensionClassCode();
// 获取类加载器
ClassLoader classLoader = findClassLoader();
// 字节码生成对象。
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 通过字节码工具类,生成对象
return compiler.compile(code, classLoader);
}
createAdaptiveExtensionClassCode
这个代码非常长 ,大家看个大概就可以了。我也是为了帮助自己理解,看下他的这个类是怎么拼接的。
private String createAdaptiveExtensionClassCode() {
// 构建一个字符创拼接对象
StringBuilder codeBuilder = new StringBuilder();
// 获取实现了SPI接口的方法
Method[] methods = type.getMethods();
// 方法上是否有@Adaptive注解
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
// 判断方法上是否有Adaptive注解
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
//
}
//如果没有方法的头上有Adaptive注解 , 那么直接不通过,因为这里是给带有Adaptive注解的方法生成代理对象
if (!hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
codeBuilder.append("package ").append(type.getPackage().getName()).append(";"); // 报名
codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";"); // 导入包
codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {"); // 定义类名
for (Method method : methods) {
Class<?> rt = method.getReturnType(); // 方法的返回值类型
Class<?>[] pts = method.getParameterTypes(); // 方法的入参 类型集合
Class<?>[] ets = method.getExceptionTypes(); // 方法抛出的异常集合
// 代码省略。。
}
codeBuilder.append("\n}");
if (logger.isDebugEnabled()) {
logger.debug(codeBuilder.toString());
}
return codeBuilder.toString();
}
代码太长了,我就不贴了,最终生成的字符串如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
// 重点在这里。。。
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
// 重点在这里。。。
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
扩展实现类名称, 是否还记得Protocol.class类上的SPI注解,该注解里面指定了Protocol的协议类型为dubbo, 因此,在这里,如果说,传入的参数中没有动态指定其他的协议类型的话,那么默认的协议类型就是dubbo, 也就是说dubbo允许在运行过程中使用其他的协议类型,
String extName = (url.getProtocol() == null ? “dubbo” : url.getProtocol());
上面的扩展实现类,最重要的一行代码,就是
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(
com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName)
之前SPI的第一篇文章我们了解到,ExtensionLoader.getExtensionLoader 这个方法中 , 针对ExtensionLoader的创建仅限在第一次,第二次获取,会直接从缓存中获取,因此不存在性能问题。 关键在于getExtension,这个方法我们之前没有了解过,下面看一下这个方法的实现。
getExtension
public T getExtension(String name) {
// 判断扩展名称不为空。
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
// 如果名称等于true的话,那么直接返回默认的扩展实现类
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从缓存中获取当前name所属的Holder
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
// 不存在,则创建一个Holder
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
// 获取holder里面包含的实例信息
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
private T createExtension(String name) {
// 从getExtensionClasses中根据当前名称获取实例信息
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
// 找不到,则说明当前传入的扩展名称,没有在SPI扩展文件中存在
throw findException(name);
}
try {
// 从缓存map中获取实例想你想
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 为空,则进行实例化,并放入map中。
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 进行依赖注入,这个在上文中是讲过的。
injectExtension(instance);
// 装饰器类,用来增加扩展实现。
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
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 + ") could not be instantiated: " + t.getMessage(), t);
}
}
getExtensionClasses()方法返回的map数据如下,以Protocol为例
filter : com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener : com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock : com.alibaba.dubbo.rpc.support.MockProtocol
dubbo : com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm : com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi : com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian : com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift : com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached : com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis : com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest : com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry : com.alibaba.dubbo.registry.integration.RegistryProtocol
qos : com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
通过"dubbo"获取到的就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol实例。
在上面的代码中有几行代码比较重要,关系到后面的代码阅读
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
获取到缓存起来的cachedWrapperClasses , Protocol的装饰器类有下面三个
class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
class com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
也就是说DubboProtocol要经过这三个类进行装饰,包装之后返回,由于cachedWrapperClasses是一个CurrentHashSet ,所以他内部是不保证顺序的。 Wrapper类进行装饰的顺序是不一致的,所以最终instance返回的有可能是ProtocolListenerWrapper
ProtocolFilterWrapper
QosProtocolWrapper
中的任何一个,这个一定要注意,后面看服务暴露的代码的时候,首先要经过这三个类,最后才会执行DubboProtocol。
精品源码
1
前文回顾
上一篇文章我们讲到,扩展实现类的类上如果写上了@Adaptive注解,则可以直接取得扩展实现类对象,其他的需要通过字节码生成技术来生成对象,接着上文的代码讲解,调用createAdaptiveExtensionClass方法生成扩展实现类对象
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 获取扩展实现类的代码code, 这个类里面是拼接的字符穿
String code = createAdaptiveExtensionClassCode();
// 获取类加载器
ClassLoader classLoader = findClassLoader();
// 字节码生成对象。
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 通过字节码工具类,生成对象
return compiler.compile(code, classLoader);
}
createAdaptiveExtensionClassCode
这个代码非常长 ,大家看个大概就可以了。我也是为了帮助自己理解,看下他的这个类是怎么拼接的。
private String createAdaptiveExtensionClassCode() {
// 构建一个字符创拼接对象
StringBuilder codeBuilder = new StringBuilder();
// 获取实现了SPI接口的方法
Method[] methods = type.getMethods();
// 方法上是否有@Adaptive注解
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
// 判断方法上是否有Adaptive注解
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
//
}
//如果没有方法的头上有Adaptive注解 , 那么直接不通过,因为这里是给带有Adaptive注解的方法生成代理对象
if (!hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
codeBuilder.append("package ").append(type.getPackage().getName()).append(";"); // 报名
codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";"); // 导入包
codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {"); // 定义类名
for (Method method : methods) {
Class<?> rt = method.getReturnType(); // 方法的返回值类型
Class<?>[] pts = method.getParameterTypes(); // 方法的入参 类型集合
Class<?>[] ets = method.getExceptionTypes(); // 方法抛出的异常集合
// 代码省略。。
}
codeBuilder.append("\n}");
if (logger.isDebugEnabled()) {
logger.debug(codeBuilder.toString());
}
return codeBuilder.toString();
}
代码太长了,我就不贴了,最终生成的字符串如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
// 重点在这里。。。
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
// 重点在这里。。。
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
扩展实现类名称, 是否还记得Protocol.class类上的SPI注解,该注解里面指定了Protocol的协议类型为dubbo, 因此,在这里,如果说,传入的参数中没有动态指定其他的协议类型的话,那么默认的协议类型就是dubbo, 也就是说dubbo允许在运行过程中使用其他的协议类型,
String extName = (url.getProtocol() == null ? “dubbo” : url.getProtocol());
上面的扩展实现类,最重要的一行代码,就是
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(
com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName)
之前SPI的第一篇文章我们了解到,ExtensionLoader.getExtensionLoader 这个方法中 , 针对ExtensionLoader的创建仅限在第一次,第二次获取,会直接从缓存中获取,因此不存在性能问题。 关键在于getExtension,这个方法我们之前没有了解过,下面看一下这个方法的实现。
getExtension
public T getExtension(String name) {
// 判断扩展名称不为空。
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
// 如果名称等于true的话,那么直接返回默认的扩展实现类
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从缓存中获取当前name所属的Holder
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
// 不存在,则创建一个Holder
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
// 获取holder里面包含的实例信息
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
private T createExtension(String name) {
// 从getExtensionClasses中根据当前名称获取实例信息
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
// 找不到,则说明当前传入的扩展名称,没有在SPI扩展文件中存在
throw findException(name);
}
try {
// 从缓存map中获取实例想你想
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 为空,则进行实例化,并放入map中。
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 进行依赖注入,这个在上文中是讲过的。
injectExtension(instance);
// 装饰器类,用来增加扩展实现。
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
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 + ") could not be instantiated: " + t.getMessage(), t);
}
}
getExtensionClasses()方法返回的map数据如下,以Protocol为例
filter : com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener : com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock : com.alibaba.dubbo.rpc.support.MockProtocol
dubbo : com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm : com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi : com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian : com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift : com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached : com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis : com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest : com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry : com.alibaba.dubbo.registry.integration.RegistryProtocol
qos : com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
通过"dubbo"获取到的就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol实例。
在上面的代码中有几行代码比较重要,关系到后面的代码阅读
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
获取到缓存起来的cachedWrapperClasses , Protocol的装饰器类有下面三个
class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
class com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
也就是说DubboProtocol要经过这三个类进行装饰,包装之后返回,由于cachedWrapperClasses是一个CurrentHashSet ,所以他内部是不保证顺序的。 Wrapper类进行装饰的顺序是不一致的,所以最终instance返回的有可能是ProtocolListenerWrapper
ProtocolFilterWrapper
QosProtocolWrapper
中的任何一个,这个一定要注意,后面看服务暴露的代码的时候,首先要经过这三个类,最后才会执行DubboProtocol。