扩展点机制

扩展点的配置

(1)根据关键字读取配置,获取具体的实现类

比如在 dubbo-demo-provider.xml 文件中配置:

<dubbo:service protocol="rmi" interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />

则会根据rmi去读取具体的协议实现类RmiProtocol.java 。

(2)@SPI和@Adaptive

  • @SPI注解,可以认为是定义默认的实现类;
  • @Adaptive注解:该注解打在接口的方法上;调用ExtensionLoader.getAdaptiveExtension()获取适配类,会先通过前面的过程生成 java 的源代码,在通过编译器编译成 class 加载。

扩展点加载流程

以如下例子为例:

private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

在这个语句中,我来分析,首先 Protocol 类带有 SPI 注解,因此我们可以确认默认是使用 ***dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol ***作为默认扩展点。

@SPI("dubbo")
public interface Protocol {
/**
* 获取缺省端口,当用户没有配置端口时使用。 *
*
* @return 缺省端口
*/
int getDefaultPort(); /**
* 暴露远程服务:<br>
* 1. 协议在接收请求时,应记录请求来源方地址信息: RpcContext.getContext().setRemoteAddress();<br>
* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没 有区别。<br>
* 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br> *
*
* @param <T> 服务的类型
* @param invoker 服务的执行体
* @return exporter 暴露服务的引用,用于取消暴露
* @throws RpcException 当暴露服务出错时抛出,比如端口已占用
*/
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
/**
* 引用远程服务:<br>
* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行
同URL远端export()传入的Invoker对象的invoke()方法。<br>
* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请
求。<br>
* 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br> *
*
*
*
*
*
*/
@param <T>服务的类型
@param
type 服务的类型
@param
url 远程服务的URL地址
@return
invoker 服务的本地代理
@throws
RpcException 当连接服务
供方失败时抛出
@Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; /**
* 释放协议:<br>
* 1. 取消该协议所有已经暴露和引用的服务。<br>
* 2. 释放协议所占用的所有资源,比如连接和端口。<br> * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
*/
void destroy();
}

这里对应的函数为:

@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive
instance:" + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}

这里看到 createAdaptiveExtension 函数:

@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
return injectExtension((T)
getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
} private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

动态生成类

而 cachedAdaptiveInstance 类则是若有 cachedAdaptiveClass 对象,则直接返回,否则通 过生成类文件,然后 complier 出来的,其文件内容如下:从该文件可以很容易看出只有标 注了@Adaptive 注释的函数会在运行时动态的决定扩展点实现;

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative 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.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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(co m.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
} public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
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(co m.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}

此时我们分析 getExtension 函数:

/**
* 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
*
* @param name * @return
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(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,

@SuppressWarnings("unchecked")
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, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
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 + ") couldnotbeinstantiated:" + t.getMessage(),
t);
}
}

而这里 injectExtension 类,则是为生成的 instance 注入变量; 其目标是搜索所有 set 开头,同时只有一个入参的函数,执行该函数,对变量进行注入;

private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() +
method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt,
property);
if (object != null) {
method.getName()
" + e.getMessage(), e);
}
}
}
}
} catch(Exception e){
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " +
+" of interface " + type.getName() + ":
logger.error(e.getMessage(), e);
}
return instance;
}

此时我们的目光转到如下一段代码:

Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if(wrapperClasses !=null&&wrapperClasses.size()>0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T)
wrapperClass.getConstructor(type).newInstance(instance));
}
}

在分析这段代码的作用之前,我们先来分析一下Set<Class<?>> cachedWrapperClasses是如何被赋值的;

此时我们转到,private void loadFile(Map<String, Class<?>> extensionClasses, String dir) 函数:



分析:这里实际上是如果该类带有 Adaptive 注解,则认为是 cachedAdaptiveClass;若该 类没有 Adaptive 注解,则判断该类是否带有参数是 type 类型的构造函数,若有,则认为是wrapper 类;

于是我们分析如下文件 dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol 其内容为:

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper

listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper

mock=com.alibaba.dubbo.rpc.support.MockProtocol

我们分析这三个类,会发现 mock 类没有参数为 Protocol 的自定义参数,而其他两个均有; 此时我们返回到 createExtension 函数:

    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) { for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
} }

此时可以发现这里对 instance 加了装饰类;对于 Protocol 来说加了两个装饰类 ProtocolFilterWrapper 和 ProtocolListenerWrapper;

也就/injectExtension 实例化包装类,并注入接口的适配器, 注意这个地方返回的是最后一 个包装类。

扩展点装饰

Filter

最新文章

  1. 【UWP开源】图片编辑器,带贴图、滤镜、涂鸦等功能
  2. c#动态编译并动态生成dll
  3. KI的斐波那契_DFS
  4. 使用 archetype插件创建maven目录结构
  5. [2015hdu多校联赛补题]hdu5378 Leader in Tree Land
  6. WCF 初识(一)
  7. 搭建maven+spring+mybatis工程
  8. 【服务器运维】Windows Server 2008 R2 下配置证书服务器和HTTPS
  9. Uber在华从沸点到冰点 搞定这些才能继续走下去
  10. 转 如何使用V7包中ActionBar(Eclipse版)
  11. Docker学习总结(一)
  12. UNIX环境高级编程——进程基本概述
  13. DirectX11--HLSL编译着色器的三种方法
  14. SuperMaze(Hello World 团队)Alpha版使用说明
  15. 原生js可视加载图片、延迟加载、懒加载
  16. MySQL:数据类型介绍
  17. mybatis学习七 typeAliases 别名
  18. MySql and Oracle Data Type Mappings
  19. JavaScript权威指南——跳转语句
  20. Redis实战(三)CentOS 7上Redis主从复制

热门文章

  1. C博客作业01--分支顺序结构
  2. 模板 - 部分C++库
  3. a标签伪类选择器+过度模块
  4. 微信小程序实例:分享给一个人还是分享到群的判断代码
  5. postgres安装pg_buffercache扩展
  6. Java基础 throw 抛出异常后,用try...catch捕获
  7. &lt;img&gt;的title和Alt有什么区别?
  8. linux系统(centos6)的目录结构
  9. xmlns:amq=&quot;http://activemq.apache.org/schema/core&quot;报错
  10. c-lodop回调函数简短问答及相关博文