东莞网站优化一般多少钱,无锡集团网站建设公司,广东汕头网络科技有限公司,网站排名按天付费IOC#xff08;inversion of control#xff09;即控制反转#xff0c;是面向对象编程中的一种设计原则#xff0c;可以用来减低计算机代码之间的耦合度。IOC-golang 是一款服务于Go语言开发者的依赖注入框架#xff0c;基于控制反转思路#xff0c;方便开发人员搭建任何… IOCinversion of control即控制反转是面向对象编程中的一种设计原则可以用来减低计算机代码之间的耦合度。IOC-golang 是一款服务于Go语言开发者的依赖注入框架基于控制反转思路方便开发人员搭建任何 Go 应用。
在本文中我不会罗列这个项目的种种功能与实现而是站在开发者的角度谈一谈我认为 Go 应用开发的“理想姿态”。
项目背景
在面向对象编程的思路下开发者需要直接关心对象之间的依赖关系、对象的加载模型、对象的生命周期等等问题。对于较为复杂的业务应用系统随着对象数目增长对象之间的拓扑关系呈指数级增加如果这些逻辑全部由开发人员手动设计和维护将会在应用内保存较多业务无关的冗余代码影响开发效率提高代码学习成本增加了模块之间的耦合度容易产生循环依赖等等问题。
随着开发者的增多设计模型的复杂化将会产生对象管理框架的诉求例如 Java 生态的 Spring 框架其设计的核心就是控制反转思路从而为开发者提供依赖注入、配置注入、生命周期管理等能力。Go 语言生态在开源侧也有较多基于该思路的实现但普遍能力较为单一相比于我们的设计思路 在可扩展性、易用性等方面有所不足。
IOC-golang 不是 Go 语言实现的 Spring 框架
我们致力于打造一款针对 Go 开发人员的框架它适配与 Go 的语法和各种基本概念符合 Go 语言开发习惯能真正为开发人员提供编程、思考、运维、以及代码阅读上的便利。
设计思路
让我们聊一些轻松的话题。
应用开发思路
应用程序多种多样都是由开发人员一行一行代码编写出来的身为开发人员在编写代码之前一定是对接下来要写的每一行代码有初步的思考与设计。例如我身为一个 Go 开发人员如果期望编写一个web 后端服务程序那么我会怎么做
最直观的思路我需要启动一个http server用于监听某个端口并且处理http协议的请求。使用面向对象的思路我需要构建一个http server对象之后调用方法开启监听。再往下一层思考这个http server 对象如果要创建出来需要依赖一些对象这些对象可能包含多个 http handler 对象、用于可视化上报的对象、管理安全认证的对象等等。再下一层一个http handler 对象依赖的对象有负责执行序列化操作的对象、传输结构对象、业务处理对象业务处理对象又依赖一些sdk例如缓存客户端对象、数据库客户端对象等等。我们现在层层思考的过程也就是自顶向下的设计模型。
我们可以把一个复杂的应用程序根据依赖关系抽象为一个具有单起点的有向图以上面描述的场景为例我们可以画出具有如下拓扑的图。 脑海中有了这些拓扑就可以按照习惯的方式编写代码了我可能选择先把未实现的模块抽象成接口由上至下编写结构我也可能习惯自底向上开发先从最具体的底层结构入手然后用多个子结构组成一个完整的上层结构。无论选择哪种实现方案我在开发时总会关心一件事情我要开发的结构是由哪些结构组成的我把这个事情称作一个“开发单元”这也是IOC-golang 框架关心的主要问题之一。
按照常规的应用开发模式在一个“开发单元”内开发者需要关注哪些事情我们习惯于编写一个构造函数返回需要的对象这个构造函数的入参包含了一些参数以及下游依赖我们在构造函数中会把这些对象和参数拼接成一个结构再执行初始化逻辑最后返回。
我们把这个“开发单元”关心的东西按照依赖关系抽象成下图。 也就是说如果想基于一个结构构造出一个对象我们最多需要提供这三个东西就够了参数/配置、依赖的子对象和一段包含初始化逻辑的函数当然对于一些简单的结构可能只需要三者中的一两者甚至都不需要。按照这一思路开发人员可以把 “编写一个应用” 拆分成若干个 “构造一个对象”的过程二者是等价的我们都在编码的过程中潜移默化地做了这件事情。
IOC-golang 可以帮助开发者更清晰地“构造一个对象”
在使用IOC-golang 开发的过程中开发者只需要将参数、依赖对象、初始化逻辑这三要素通过注解或标签的形式标注在代码中即可完成一个结构的定义完全无需关心结构的拼装过程和依赖对象的创建过程让开发者专注于当前结构的业务逻辑。
// ioc:autowiretrue
// ioc:autowire:typenormal
// ioc:autowire:paramTypeConfig
// ioc:autowire:constructFuncNewtype RedisClient struct {client *redis.ClientServiceImpl1 Service singleton:main.ServiceImpl1 // inject Service s ServiceImpl1
}type Config struct {Address stringPassword stringDB string
}func (c *Config) New(impl *Impl) (*Impl, error) {dbInt, err : strconv.Atoi(c.DB)if err ! nil {return impl, err}client : redis.NewClient(redis.Options{Addr: c.Address,Password: c.Password,DB: dbInt,})_, err client.Ping().Result()if err ! nil {return impl, err}impl.client clientreturn impl, nil
}
基于 IOC-golang 框架开发的一个包含了 redis 客户端的结构 RedisClient通过注解或标签指定了参数、初始化逻辑和依赖的对象。
如果按照常规的开发方式开发者还需要要额外做这些事情手动从配置文件读取 Config对象的所有字段手动把 ServiceImpl1 对象创建出来手动拼装 RedisClient 对象然后调用初始化逻辑。而这些通用的逻辑都被 ioc-golang 封装好了。
IOC-golang 可以帮助开发者管理设计模型
对象如何被获取和注入参数从哪里加载接口由谁实现以及上面提到的“是否单例模型”等等问题只需要开发者在结构注解中标注好使用的自动装载模型就可以达到期望的效果。开发者也可以定制化需要的自动装载模型。
// ioc:autowiretrue
// ioc:autowire:typesingletontype App struct {ServiceImpl1 Service singleton:main.ServiceImpl1 // inject Service s ServiceImpl1 implementationRedisClientPtr *RedisClient normal:,addresslocalhost:6379db0 // inject RedisClient struct pointer
}
基于IOC-golang框架开发的一个使用单例模式的对象 APP其依赖一个 Service 接口该接口期望被main.ServiceImpl1单例模式结构注入APP 还依赖一个*RedisClient 结构体指针期望以多例模式注入并传入了参数。参数也可以从配置文件的结构默认位置读取。
如果按照常规的开发方式开发者需要额外手动读取参数并创建依赖的 RedisClient 对象手动组装 APP对象维护单例模型指针提供单例模型的构造函数等这些逻辑都被 IOC-golang 封装好了。
对象生命周期
上一节所说的是静态的编码过程这一节我们来聊一聊一个对象从静态的编码到应用运行过程中被加载直到销毁的整个生命周期。
一个 Go 对象的完整生命周期一般包含以下几个环节
结构定义结构提供者编写结构的字段与函数。参数传入与依赖对象加载对象创建的准备阶段获取到依赖的参数获取到所有依赖的下游对象。对象创建与初始化对象的组装过程组装完成后执行必要的初始化逻辑。对象使用对象的函数被使用者调用对象销毁对象被销毁
两个视角
上述对象的生命周期是站在对象的角度来观察的。我们还可以从结构开发者视角和结构使用者视角来观察。为了表述的更为形象我们可以用 “产品说明书” 来比喻结构的全部信息。 结构开发者视角撰写产品说明书
我负责定义结构的字段和函数 我负责明确依赖的下游对象、依赖的参数字段。 我负责明确参数应该从哪里加载例如从标签加载从配置中某个位置加载从 API 传入的参数中加载。 我负责明确结构的依赖注入模型比如单例模型。 我负责定义对象的初始化逻辑 我负责定义对象的销毁逻辑
结构使用者视角阅读产品说明书按照说明书的内容使用结构
我负责明确要使用哪个结构。找到对应的产品说明书 我负责使用一种“产品说明书”中支持的自动装载模型例如单例模型。 我负责明确“产品说明书”中给定的结构依赖参数。 我负责使用一种“产品说明书”里支持的参数加载方式来加载结构依赖参数加载方式可能包括从标签加载从配置中某个位置加载从 API 传入的参数中加载。 我负责使用一种对象获取方式来获取对象实例例如通过 API 获取通过标签注入获取。 我负责调用对象函数。 我负责触发对象销毁逻辑。
上述的两个视角是开发者在面向对象编程的开发过程中一定会考虑的。ioc-golang 框架在设计中明确了这两个视角。让一个结构的生命周期不再是一串面向过程的操作而是两侧责任明确的开发模型。
可扩展性
IOC-golang 全面拥抱可扩展性我们希望您在框架内接触到的任何概念都是可横向扩展的。可扩展性不意味着任何事情都要手动编写而是在拥有足够预置实现方案的基础之上支持针对特殊场景的定制化。如果您的方案足够通用也可以提交至开源侧让更多人可以享受你的方案带来的便利。
IOC-golang 的可扩展性体现在多个方面其中最重要的是依赖注入过程的可扩展性这也是框架能的核心能力。
依赖注入的可扩展性包含三个维度从具体到抽象分别是
对象的可扩展性
对象的可扩展性即针对确定的一个结构非单例你可以通过传入不同的参数来获取多个期望的对象。这个过程往往被结构使用者关注他需要思考如何传入参数获得对象调用对象从而实现正确的业务逻辑。通过这一可扩展性结构使用者可以扩展出多个对象实例。
结构的可扩展性
结构的可扩展性即针对一个确定的自动装载模型你可以通过定义自己的结构描述信息将你的结构体注册在框架上以供使用。这个过程是结构提供者关心的他需要思考选用哪个自动装载模型思考提供的结构的全部生命周期信息。通过这一可扩展性结构提供者可以为框架注入多种多样的结构这些结构都会按照被选择的自动装载模型执行加载逻辑。
框架提供了一些预置的结构例如 redis 客户端、gorm客户端等等开发者可以直接传入参数注入或通过API获取直接使用但这些预置的结构一定无法覆盖业务需求的。开发者注册自己的任何结构到框架上都是使用了结构的可扩展性这些被注册的结构和框架提供的预置结构都是同一层面的概念。
自动装载模型的可扩展性
自动装载模型描述了一类结构的装载方式例如是否单例模型、参数从哪里加载、注入标签应该符合什么格式等等。这个过程也是结构提供者关心的。
框架提供了一些预置的自动装载模型例如单例模型、多例模型、配置模型、rpc 模型等开发者可以根据按照业务需要将一个或多个结构注册在期望的自动装载模型上当已有的自动装载模型不足以适配业务场景开发者可以调用API进行定制化。
下图描述了框架可扩展性的架构 小结
IOC-golang 框架关注
一个问题一个对象如何被创建。两个角度结构开发者视角结构使用者视角。三个维度扩展自动装载模型、结构、对象。
项目层级与功能
项目层级
IOC-golang 是一个语言绑定的框架处于应用开发的最高层直接由开发人员操作。一些开发常用的组件所处层级如下图所示。这也解释了IOC-golang 可以称为服务于 Go 开发者的 ioc 框架而不是 Spring 框架的 Go 语言实现。 在软件架构的层级中往往层级越高的模块越形象其使用体验更佳能力覆盖范围更广对于同语言下层的模块扩展性更好但会造成语言绑定。层级越低的模块越抽象其泛用性更佳更具备通用型更容易做到语言无关。
主要功能
主要功能有二即 IOC 和 AOP分别代表了面向开发的“依赖注入”能力和面向运维的 “代理结构 AOP 层”。
依赖注入能力IOC依赖注入能力
强大、易用、可扩展的依赖注入能力是框架的核心功能。
开发者可以将任何结构体注入至标签字段
// ioc:autowiretrue
// ioc:autowire:typesingletontype App struct {ServiceStruct *ServiceStruct singleton: // inject ServiceStruct struct pointer
}
将任何结构体注入至任何接口
// ioc:autowiretrue
// ioc:autowire:typesingletontype App struct {ServiceImpl1 ServiceInterface singleton:main.ServiceImpl1 // inject ServiceInterface s ServiceImpl1 implementation
}
可以通过 API 的方式获取任何已注册的结构体
// 该函数由 iocli 工具自动生成
func GetApp() (*App, error) {i, err : singleton.GetImpl(util.GetSDIDByStructPtr(new(App)), nil)if err ! nil {return nil, err}impl : i.(*App)return impl, nil
}
灵活的参数传递能力
本框架支持通过标签、API、配置三种方式传入依赖参数并允许开发者扩展这一能力。
标签参数传递传递 address, db 等结构所需参数
// ioc:autowiretrue
// ioc:autowire:typesingletontype App struct {NormalDB3Redis normalRedis.ImplIOCInterface normal:,address127.0.0.1:6379db3
}
通过 API 进行参数传递
func main() {client : redis.NewClient(redis.Options{Addr: p.RedisAddr,})
}
通过配置进行参数写入ioc_golang.yaml, 配置 github.com/alibaba/ioc-golang/extension/normal/nacos.Impl 结构的构造参数
autowire:normal:github.com/alibaba/ioc-golang/extension/normal/nacos.Impl:my-nacos:param:nacosclientparam:clientconfig:appKey: appKeyserverconfigs:- ipaddr: 127.0.0.1port: 8848
结构代理层AOP
本框架提供的 iocli 命令行工具会识别结构注解从而生成注册代码、结构代理层、结构专属接口等信息减少开发人员需要编写的代码量。 我们以这样一个结构为例
// ioc:autowiretrue
// ioc:autowire:typesingletontype Impl1 struct {
}func (i *Impl1) Hello(req string) string {return req
}
开发者可以通过执行iocli gen命令一键为工程内所有标注的结构生成代码生成位于结构相同目录的zz_generated.ioc.go其内容包括
结构注册代码会以结构描述符的形式将结构生命周期注册到框架上。
func init() {singleton.RegisterStructDescriptor(autowire.StructDescriptor{Factory: func() interface{} {return Impl1{}},})
}
结构专属接口命名为 $(结构名)IOCInterface
type Impl1IOCInterface interface {Hello(req string) string
}
结构代理层存根
框架会为所有结构提供代理层在面向接口编程的场景下所有请求都会经过代理层扩展出强大的运维能力。
type impl1_ struct {Hello_ func(req string) string
}func (i *impl1_) Hello(req string) string {return i.Hello_(req)
}
结构获取API
// 获取结构指针
func GetImpl1() (*Impl1, error) {i, err : singleton.GetImpl(util.GetSDIDByStructPtr(new(Impl1)), nil)if err ! nil {return nil, err}impl : i.(*Impl1)return impl, nil
}
// 获取包装了代理层的专属接口
func GetImpl1IOCInterface() (Impl1IOCInterface, error) {i, err : singleton.GetImplWithProxy(util.GetSDIDByStructPtr(new(Impl1)), nil)if err ! nil {return nil, err}impl : i.(Impl1IOCInterface)return impl, nil
}
以上代码均由工具自动生成开发者只需使用即可。通过这些代码展示我想传达给读者一个信息就是 IOC-golang 可以管理任何对象的代理存根这也就意味着我们可以用这一层代理做任何想做的事情。其中就包括框架已经实现的能力
结构展示可以展示所有加载到框架的结构、方法列表接口参数动态监听可以监听运行态的 go 进程展示当前正在执行的函数、参数值、返回值。链路追踪可以获取一个请求的完整调用链路获取分布式场景下的跨进程调用链路从而分析性能瓶颈。
但这些只是运维这个庞大的话题的冰山一角我们基于结构代理 AOP 层 可以做任何我们能想到的事情
链路可视化、分布式应用拓扑展示告警故障注入问题诊断...
这可以由我们一同设计一同实现一同见证
丰富的组件
IOC-golang 目前已预置一系列开发常用sdk供直接注入。覆盖数据库、缓存、中间件、RPC 等等领域。目前已支持的组件有
NacosRocketmqRedisgRPCGORMDubbo3HttpServer
在未来将会支持更丰富的常用 Go 开发 SDK为开发者提供全家桶式的开发体验。
上述只是简单的介绍更详细的内容请参考文末提供的项目示例。具体模块的更详细设计与介绍例如基于IOC思路的RPC分布式全接口链路追踪能力将在后续文章中与大家见面敬请期待。
主要功能主要功能有二即 IOC 和 AOP分别代表了面向开发的“依赖注入”能力和面向运维的 “代理结构 AOP 层”。
愿景
IOC-golang 是一个崭新的项目从第一行代码的编写到今天不过一个月的时间。
我们的愿景是让 IOC-golang 成为 Go 应用开发的首选框架让 Go 开发人员更贴近业务逻辑减少冗余代码增加代码的易读性让 Go 生态的 “面向对象编程” 进入自动挡时代。
主要功能主要功能有二即 IOC 和 AOP分别代表了面向开发的“依赖注入”能力和面向运维的 “代理结构 AOP 层”。
如何贡献
项目初创十分期待能与感兴趣的开发者共创一片天地。
项目github地址 github.com/alibaba/ioc-golang
项目文档ioc-golang.github.io
项目示例ioc-golang/example
作者 | 李志信(冀锋)
原文链接
本文为阿里云原创内容未经允许不得转载。