【dubbo系列】 09-dubbo AOP 机制

前言

对于使用过 Spring 技术的同学一定对 AOP 不会陌生,通常使用AOP 在方法的前后插入其它的逻辑。如使用 Spring AOP 实现日志记录、鉴权等操作。

那在 dubbo 中是如何实现这样的功能的呢?在 dubbo 中有一种特殊的类叫做 Wrapper 类。通过装饰者模式,使用包装类包装原始的扩展类实例。在原始扩展点前后插入其它的逻辑,从而实现类似 AOP 的功能。

此篇文章是对 【dubbo系列】 08-dubbo SPI 机制 的一个补充。

什么是 Wrapper

那什么样的类才是一个 Dubbo 中的一个 Wrapper 类呢?下面可以从 ExtensionLoader 的源码中了解到如何判断一个 Wrapper 类。其源码基于 dubbo 2.7.2.


/**
* 判断是否是一个包装类
*/
private boolean isWrapperClass(Class<?> clazz) {
        try {
            //
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

从源码中很容易之到,通过 clazz.getConstructor(type) 方法,获得其拷贝的构造函数。其中type 是接口,clazz是接口的扩展实现类。

我们以 dubbo 中的 ProtocolFilterWrapper 类进行分析,具体什么样的类可以称为一个Wrapper 类。


public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
    ...

}

其中,Protocol 是一个接口,ProtocolFilterWrapper 是其一个实现类,ProtocolFilterWrapper 有一个构造方法,其参数是 Protocol ,我们就说其是一个复制的构造函数,ProtocolFilterWrapper 就是一个 Wrapper类。

如何配置一个 Wrapper

在Dubbo 中 Wrapper 也是一个扩展类,同其它扩展类的配置是一样的,也是在 META-INF/dubbo 文件夹下配置。比如 ProtocolFilterWrapper 其配置就在 META-INF\dubbo\internal\com.alibaba.dubbo.rpc.Protocol 下面。


filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol

org.apache.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=org.apache.dubbo.rpc.protocol.thrift.ThriftProtocol
native-thrift=org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol
memcached=org.apache.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=org.apache.dubbo.rpc.protocol.redis.RedisProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
xmlrpc=org.apache.dubbo.xml.rpc.protocol.xmlrpc.XmlRpcProtocol
registry=org.apache.dubbo.registry.integration.RegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

Wrapper 是何时加载及使用的

其源码在 ExtensionLoader 类中,关于 ExtensionLoader 可以参考 【dubbo系列】 08-dubbo SPI 机制

private void cacheWrapperClass(Class<?> clazz) {
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

其流程是在加载配置文件时,如果是一个wrapper类,则把这个缓存起来。其不会加入“配置项名称”到“配置类”的映射关系表中。

当在调用 getExtension 方法去获得一个扩展类实例时,会做以下操作:查询是否有 Wrapper类,如果有包装类,就会用反射创建一个 Wrapper 的实例,然后注入一个原始的扩展类实例。

源码如下:


Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
    for (Class<?> wrapperClass : wrapperClasses) {
        // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例
        // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
    }
}

简单实例

  • 定义接口、Wrapper 和实现类

      @SPI
      public interface HelloService {
    
          void sayHello();
      }
    
      public class HelloServiceWrapper implements HelloService {
    
          private HelloService helloService;
    
          public HelloServiceWrapper(HelloService helloService) {
              this.helloService = helloService;
          }
    
          @Override
          public void sayHello() {
              System.out.println("wrapper begin");
              this.helloService.sayHello();
              System.out.println("wrapper end");
          }
      }
    
      public class HelloServiceImpl implements HelloService {
          @Override
          public void sayHello() {
              System.out.println("hello");
          }
      }
    
    

    例子中我们定义了一个接口,一个 Wrapper 类,以及一个普通的实例类。HelloServiceWrapper 是一个 Wrapper 类。重写了sayHello 方法,在其前后增加了打印语句。 HelloServiceImpl 是一个普通扩展类。

  • 增加配置文件

    META-INF/dubbo 目录下新增文件,文件名为接口的全限名。

      wrapper = com.joyxj.spi.HelloServiceWrapper
      helloServiceImpl = com.joyxj.spi.HelloServiceImpl
    
  • 测试

      @Test
      public void test() {
          ExtensionLoader<HelloService> loader = ExtensionLoader.getExtensionLoader(HelloService.class);
          HelloService helloService = loader.getExtension("helloServiceSecondImpl");
          helloService.sayHello();
      }
    

    执行方法,输出:

      wrapper begin
      hello
      wrapper end 
    

   转载规则


《【dubbo系列】 09-dubbo AOP 机制》 孤独如梦 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
【dubbo系列】 10-spring boot 整合 dubbo 【dubbo系列】 10-spring boot 整合 dubbo
环境准备本文基于 dubbo 2.7 与 spring boot 的整合。笔者提前准备的环境: jdk 1.8 maven 3.3 zookeeper 3.4.13 项目整体结构- dubbo-spring-boot-demo
2019-06-26
下一篇 
【dubbo系列】 08-dubbo SPI 机制 【dubbo系列】 08-dubbo SPI 机制
简介Dubbo 未使用 Java SPI,而是重新实现了一套更强的 SPI 机制。其相关逻辑封装在 ExtensionLoader 类中,通过 ExtensionLoader 可以加载指定的实现类。其所需要的配置文件需放置在META-INF
2019-06-18
  目录