一、spring-cloud-feign 中的代理模式

Feign 是 Spring cloud 微服务架构组件之一,Feign 功能主要是远程 API 调用。
Feign 相关使用案例代码如下:

[设计模式]-[结构型]-代理模式-应用案例 - 图1

上述 Feign APi 调用程序代码中,

存在两个服务,一个是 consumer 一个是 provider

consumer通过Feign 发起远程调用,调用 provider 中的 API 接口 /hello/name
通过上述代码能够知道,consumer 发起远程 API 调用,代码直接是调用的 Feign Client 接口类 HelloClient
总结 Feign 案例代码信息能够得到三点:
1、远程服务名称 provider
2、远程 API 接口 /hello/name 3、代码定义的API调用,只是一个接口类
通过上述信息能够知道,Feign 需要通过这些信息发起远程 API 调用, Feign 需要做的就是将 provider 置换成 IP + port的具体请求地址,然后结合 API 接口 /hello/name 拼接请求 url ,发起 HTTP 请求。
而完成这些功能的 Feign Client 也就是 HelloClient 只是一个接口,该接口只是做了方法的定义,而真正实现功能的是 Feign Client 的代理类。
以上述代码案例,Feign Client 接口,也就是 HelloClient 在进行实例化加入到 Spring IOC 容器时,会构建一个代理对象,该代理对象的逻辑图如下:

[设计模式]-[结构型]-代理模式-应用案例 - 图2
Feign Clicent 最终实例化的对象,有两部分内容需要关注:

  • HardCodedTarget
  • ReflectiveFeign.FeignInvocationHandler

分别来看一下 **HardCodedTarget** 对象 和 **ReflectiveFeign.FeignInvocationHandler**

HardCodedTarget

代码如下

[设计模式]-[结构型]-代理模式-应用案例 - 图3

该对象存储了远程调用的相关信息

  • type
    存储的就是 Feign Client 类,也就是 HelloClient
  • name
    存储的就是远程服务名,也就是 provider
  • url
    存储的就是远程API调用,也就是 http://provider

ReflectiveFeign.FeignInvocationHandler

[设计模式]-[结构型]-代理模式-应用案例 - 图4

如上述代码,该对象是典型的的 JDK 动态代理类,真正进行 请求发送的代码在该代理类中完成。

具体 Http 请求操作,在 `dispatch.get(method).invoke(args) 中完成。 其中 `dispatch.get(method)` 获取到的 对象为 `SynchronousMethodHandler` ` 这里面包含了负载均衡,链路追踪等,自行进行扩展。

总结

在 SPring Cloud 中使用 Feign 组件进行远程API 调用,只需要定义 Feign Client 接口类就可以进行 API 调用,如下图:

[设计模式]-[结构型]-代理模式-应用案例 - 图5
其原理是因为,代码底层通过 JDK 动态代理,为 Feign Client 做了代理,所有操作都在代理中进行。

二、Mybatis 中 Mapper 接口动态代理

Mybatis 面试题中,最喜欢问的一个问题,XXXMapper.java 接口,只是一个接口,为什么直接调用就能够发起数据库请求?

Mybatis 中最重要的类就是 **Configuration**,构建 Configuration 的类为 XMLConfigBuilder ,在进行 Configuration 构建的时候,相关的 **XXXMapper.java** 接口同样会被读取存储在 Configuration 中的 **MapperRegister**

MapperRegister 注册 Mapper.java 接口的相关代码如下:

[设计模式]-[结构型]-代理模式-应用案例 - 图6

在进行 XXXMapper.java 信息缓存的时候,Mybatis 真实缓存的是 **MapperProxyFactory**类,代理工厂类。

来看看代理工厂类具体内容

[设计模式]-[结构型]-代理模式-应用案例 - 图7

如上述代码,直接根据 XXXMapper.java 接口进行代理类的创建,对应的代理类为 **MapperProxy** ,来看看代理类 MapperProxy ,具体查看其 invoke 方法,相关代码如下:

[设计模式]-[结构型]-代理模式-应用案例 - 图8

如上代码,**MapperProxy** 是一个 JDK 动态代理类,而在 **invoke** 方法中会调用 **MapperMethod#execute** 进行 SQL 语句的拼接调用。

总结

Mybatis 中针对每一个 XXXMapper.java 接口进行代理,相关逻辑图如下:

[设计模式]-[结构型]-代理模式-应用案例 - 图9

回到问题:为什么直接调用 XXXMapper.java 接口就能够进行 SQL 查询,因为在代码运行期间,XXXMapper.java 会被代理类 MapperProxy 代理,所有的操作都由 代理类来完成。

三、项目应用中的代理模式

Springframework AOP 本身实现就是 代理模式,项目中使用 springframework 框架时并用到了 AOP,就相当于使用了 代理。


【公众号】花好夜猿
wxlogo.jpg