• 仿照java的jdk动态代理实现go语言动态代理


    仿照java的jdk动态代理实现go语言动态代理

    通过学习java的jdk动态代理和Cglib动态代理,仿照jdk动态代理用go实现了一个简单的动态代理

    结构型模式

    代理模式

    代理模式中分为静态代理和动态代理。静态代理需要在编译前就要写好,而动态代理需要在运行时通过反射来实现方法增强。

    静态代理:

    1. 代理类实现和目标类相同的接口,每个类都单独编辑一个代理类。
    2. 我们需要在代理类中,将目标类中的所有方法都要重新实现,并且为每个方法都附加相似的代码逻辑。
    3. 如果要添加方法增强的类不止一个,我们需要对每个类都创建一个代理类。

    动态代理:

    1. 不需要为每个目标类编辑代理类。
    2. 在程序运行时,系统会动态地创建代理类,然后用代理类替换掉原始类。
    3. 一般采用反射实现。

    代理模式的优点:

    1. 代理模式能将代理对象与真实被调用目标对象分离。
    2. 在一定程度上降低了系统的耦合性,拓展性好。
    3. 可以起到保护目标对象的作用。
    4. 可以增强目标对象的功能。

    动态代理实现

    package pro
    
    import (
       "errors"
       "fmt"
       "reflect"
    )
    
    //提供动态调用方法接口
    type InvocationHandler interface {
       Invoke(proxy *Proxy, method *Method, args []interface{}) ([]interface{}, error)
    }
    
    //代理,用来总管代理类的生成
    type Proxy struct {
       target  interface{}        //目标类,后面的类型和java的Object一样
       methods map[string]*Method //map用来装载待增强的不同的方法
       handle  InvocationHandler  //用来暴露统一invoke接口,类似多态
    }
    
    //创建新的代理
    func NewProxy(target interface{}, h InvocationHandler) *Proxy {
       typ := reflect.TypeOf(target)          //用来显示目标类动态的真实类型
       value := reflect.ValueOf(target)       //获取目标类的值
       methods := make(map[string]*Method, 0) //初始化目标类的方法map
       //将目标类的方法逐个装载
       for i := 0; i < value.NumMethod(); i++ {
          method := value.Method(i)
          methods[typ.Method(i).Name] = &Method{value: method}
       }
       return &Proxy{target: target, methods: methods, handle: h}
    }
    
    //代理调用代理方法
    func (p *Proxy) InvokeMethod(name string, args ...interface{}) ([]interface{}, error) {
       return p.handle.Invoke(p, p.methods[name], args)
    }
    
    //用来承载目标类的方法定位和调用
    type Method struct {
       value reflect.Value //用来装载方法实例
    }
    
    //这里相当于调用原方法,在该方法外可以做方法增强,需要调用者自己实现!!!
    func (m *Method) Invoke(args ...interface{}) (res []interface{}, err error) {
       defer func() {
          //用来捕捉异常
          if p := recover(); p != nil {
             err = errors.New(fmt.Sprintf("%s", p))
          }
       }()
    
       //处理参数
       params := make([]reflect.Value, 0)
       if args != nil {
          for i := 0; i < len(args); i++ {
             params = append(params, reflect.ValueOf(args[i]))
          }
       }
    
       //调用方法
       call := m.value.Call(params)
    
       //接收返回值
       res = make([]interface{}, 0)
       if call != nil && len(call) > 0 {
          for i := 0; i < len(call); i++ {
             res = append(res, call[i].Interface())
          }
       }
       return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    测试

    package pro
    
    import (
       "fmt"
       "testing"
       "time"
    )
    
    func TestName(t *testing.T) {
       //这里对活动时长做统计
       people := &People{}   //创建目标类
       h := new(PeopleProxy) //创建接口实现类
       proxy := NewProxy(people, h)
       //调用方法
       ret, err := proxy.InvokeMethod("Work", "敲代码", "学习")
       if err != nil {
          fmt.Println(err)
       }
    
       fmt.Println(ret)
    }
    
    //目标类
    type People struct {
    }
    
    func (p *People) Work(content string, next string) string {
       fmt.Println("活动内容是:" + content + ",接下来需要做:" + next)
       return "all right"
    }
    
    //用户需要自己实现的增强内容,需要实现InvocationHandler接口
    type PeopleProxy struct {
    }
    
    //在这里做方法增强
    func (p *PeopleProxy) Invoke(proxy *Proxy, method *Method, args []interface{}) ([]interface{}, error) {
       start := time.Now()
       defer fmt.Printf("耗时:%v\n", time.Since(start))
       fmt.Println("before method")
       invoke, err := method.Invoke(args...)
       fmt.Println("after method")
       return invoke, err
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    输出

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hKFwyme-1659607080908)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220804175747463.png)]

  • 相关阅读:
    认识MT5平台:从功能到优势
    Docker 基本管理
    聊聊Mybatis的SqlSession
    es 官方诊断工具
    Go构建遇到cgo动态库时解决方案
    【模电实验】【验证性实验——单管共发射极放大电路实验】
    【优化充电】粒子群算法电动汽车充电动态优化策略【含Matlab源码 2163期】
    JS 串口 16进制数据累加和取低位
    Linux虚拟服务器-LVS-集群技术
    如何在liunx下实现一个简单的程序?
  • 原文地址:https://blog.csdn.net/qq_53267860/article/details/126164229