• 长安链源码学习v2.2.1--ioc机制(九)


    长安链技术不断迭代,距离前面的教程已经过去一年多,迭代非常多的功能与特性,本次基于2.2.1版本继续学习长安链源码,相比早期教程,给阅读带来一定复杂度的是IOC,我们先来分析它。

    1. 介绍

    关于ioc的含义,网上的介绍有很多,这里不多赘述。简单来说这是一种降低模块间耦合的方式,在运行期间动态关联对象的某种实现。

    ioc机制在长安链store 存储模块中使用较多,例如:根据配置加载区块存储引擎,文件形式、KV形式、SQL形式等。IOC代码位置在chainmaker.org/chainmaker/common/v2/container

    2. 使用方法
    2.1 简单使用1

    通过一个例子来学习,首先找一个干净的工程:

    1)定义区块存储接口

    type BlockStore interface {
    	Set(key, vaule string)
    	Get(key string) string
    }
    
    • 1
    • 2
    • 3
    • 4

    2)文件存储实现上述接口,注意NewFileStore方法的返回值是interface

    type FileStore struct {
    
    }
    
    //注意该方法的返回值是interface
    func NewFileStore() BlockStore{
    	return &FileStore{}
    }
    
    func (*FileStore) Set(key, vaule string) {
    
    }
    
    func (*FileStore) Get(key string) string {
    	return "filestore-get"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3)通过IOC实现依赖注入,通过Register方法将生成文件存储对象的方法注册到IOC中心,通过Resolvebs接口说对象与FileStore实现对象关联。(如果chainmaker.org/chainmaker/common/v2/container找不到,可用过go mod tidy拉取。)

    import (
        "fmt"
    
    	"chainmaker.org/chainmaker/common/v2/container"
    )
    
    func main() {
    	c := container.NewContainer()
    	err := c.Register(NewFileStore)
    	if err != nil {
    		panic(err)
    	}
    
    	var bs BlockStore
    	err = c.Resolve(&bs)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(bs.Get("123"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2.2 简单使用2

    1)定义区块存储接口(这步骤不变)

    type BlockStore interface {
    	Set(key, vaule string)
    	Get(key string) string
    }
    
    • 1
    • 2
    • 3
    • 4

    2)文件存储实现上述接口,注意NewFileStore方法的返回值是struct

    type FileStore struct {
    
    }
    
    //注意该方法的返回值是数据结构
    func NewFileStore() *FileStore{
    	return &FileStore{}
    }
    
    func (*FileStore) Set(key, vaule string) {
    
    }
    
    func (*FileStore) Get(key string) string {
    	return "filestore-get"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3)通过IOC实现依赖注入,这里使用container.Interface ,含义是不理会NewFileStore返回值类型,根据container.Interface中的类型绑定NewFileStore实现。

    import (
    	"chainmaker.org/chainmaker/common/v2/container"
    	"fmt"
    )
    
    func main() {
    	c := container.NewContainer()
    	var bsi BlockStore
    	err := c.Register(NewFileStore, container.Interface(&bsi))
    	if err != nil {
    		panic(err)
    	}
    
    	var bs BlockStore
    	err = c.Resolve(&bs)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(bs.Get("123"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2.3 同一接口多种实现

    增加一种BlockStore实现方式:KV数据库实现

    type KVStore struct {
    
    }
    
    //注意该方法的返回值是数据结构
    func NewKVStore() *KVStore {
    	return &KVStore {}
    }
    
    func (*KVStore ) Set(key, vaule string) {
    
    }
    
    func (*KVStore ) Get(key string) string {
    	return "kvstore-get"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    默认绑定第一个实现对象(NewFileStore),配置container.Default()优先绑定,下面的案例绑定NewKVStore

    import (
    	"chainmaker.org/chainmaker/common/v2/container"
    	"fmt"
    )
    
    func main() {
    	c := container.NewContainer()
    	var bsi BlockStore
    	err := c.Register(NewFileStore, container.Interface(&bsi))
    	if err != nil {
    		panic(err)
    	}
    
    	err = c.Register(NewKVStore, container.Interface(&bsi), container.Default())
    	if err != nil {
    		panic(err)
    	}
    
    	var bs BlockStore
    	err = c.Resolve(&bs)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(bs.Get("123"))
    }
    
    • 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
    2.3 构造方法带参数

    把KVStore删除,本Case模拟不需要。构造方法增加一个flag并打印flag的值。

    type FileStore struct {
    
    }
    
    func NewFileStore(flag string) *FileStore{
    	fmt.Println(flag)
    	return &FileStore{}
    }
    
    func (*FileStore) Set(key, vaule string) {
    
    }
    
    func (*FileStore) Get(key string) string {
    	return "filestore-get"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在Register期间补充默认参数container.Parameters, 在Resolve绑定期间可以替换该参数container.Arguments,下面例子输出为Arguments

    import (
    	"fmt"
    	
    	"chainmaker.org/chainmaker/common/v2/container"
    )
    
    func main() {
    	c := container.NewContainer()
    	var bsi BlockStore
    	err := c.Register(NewFileStore, container.Interface(&bsi), container.Parameters(map[int]interface{}{0: "Parameters"}))
    	if err != nil {
    		panic(err)
    	}
    
    	var bs BlockStore
    	err = c.Resolve(&bs, container.Arguments(map[int]interface{}{0: "Arguments"}))
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(bs.Get("123"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.4 其他

    上面几种用法是比较常见长安链中的使用方法,还有其他用法:
    1)除了上述Default方式外,可在Resolve期间使用ResolveName指定绑定实现的名称,该名称在Register期间设置。
    2)IOC单例与多例的设置Lifestyle
    3)支持构造方法参数忽略Optional
    4)构造方法的参数需要依赖其他IOC对象,依靠DependsOn绑定实现

    思考:

    在学习长安链IOC机制的时候,一直在思考使用IOC的必要性,关于这点并没有在官方资料上找到,相信官方团队有自己的考量,后续将持续关注官方文档资料,将设计思想补充到本节。

  • 相关阅读:
    如何建立线上线下相结合的数字化新零售体系?
    【PyTorch实战】生成对抗网络GAN:生成动漫人物头像
    华测监测预警系统 2.2 任意文件读取漏洞复现 [附POC]
    让我们进入面向对象的世界(三)
    ComSec作业三:RSA
    在 Excel 内使用 ODBC 消费 SAP ABAP CDS view
    linux系统部署Elasticsearch集群
    C++核心编程——P36-友元
    基于ARM架构openEuler系统通过qemu模拟器自动安装启动ARM架构的openEuler虚拟机
    Maven依赖scope为system级别部署时Jar包缺少解决
  • 原文地址:https://blog.csdn.net/xjmtxwd24/article/details/126616723