Golang Go语言-Spring 轻装上阵,重新出发 v1.0.0

发布于 1周前 作者 bupafengyu 来自 Go语言

前言

多年前曾经梦想着能有一天能在 Go 语言中使用上 Spring 那样强大的框架,幸运的是我找到了go-spring,一款类似 Spring 的库,我很喜欢它,并且在我的项目中深度使用它,它给我带来了非常酷的编程体验。

然而很不幸的是最近 go-spring 停止更新了(最近的一次提交停留在九个月之前),一些问题无法得到有效的解决,因此我决定创建一个基于 go-spring 的开源库,我为它修复了一系列 bug ,并添加了一些新得特性,并对代码结构进行了简化,将外部生态从核心库中移除,仅保留核心功能,外部生态预计将来将作为独立的仓库存在,我将持续继续将它维护下去,也希望有感兴趣的人可以一起参与进来一起持续开发它。

仓库地址: https://github.com/go-spring-projects/go-spring

特性

  • IoC 容器: 实现了基于反射的控制反转(IoC)容器,支持结构体、函数和常量的注入。这意味着你可以使用' autowired '标签来自动注入依赖,而不必手动管理它们。
  • 配置管理: 受 Spring 的 [@Value](/user/Value) 注释的启发,Go-Spring 允许您从多个源(如环境变量、文件、命令行参数等)获取配置项。这为配置管理带来了前所未有的灵活性。
  • 配置验证器: 通过支持自定义验证器扩展扩展了其健壮的配置管理功能。这使您能够对属性执行有效性检查,确保仅将有效的配置应用于您的应用程序。
  • 结构化日志: 使用标准库 slog 提供内置日志记录器支持,以实现有效和简化的日志记录。这种增强提供了清晰、简洁和结构良好的日志信息,有助于系统调试和性能监视。
  • 动态属性: 提供动态属性刷新功能,让您无需重新启动应用程序即可动态更新应用程序属性。它满足了需要高可用性和实时响应的应用程序的需求。
  • 依赖序事件: 按照 bean 依赖关系的顺序,确保根据对象的生命周期正确通知初始化和销毁事件。这增强了系统在其生命周期运行中的鲁棒性和可靠性。

动态属性例子

允许在运行时动态刷新属性,不仅支持基本数据类型,还支持结构、切片和 Map 类型。

package main

import ( “fmt” “log/slog” “net/http”

"github.com/go-spring-projects/go-spring/dync"
"github.com/go-spring-projects/go-spring/gs"

)

type Handler struct { Open dync.Bool value:"${server.open:=true}" }

func (h *Handler) OnInit(ctx gs.Context) error {

http.HandleFunc("/server/status", func(writer http.ResponseWriter, request *http.Request) {
	if !h.Open.Value() {
		http.Error(writer, "server closed", http.StatusNotAcceptable)
		return
	}

	fmt.Fprint(writer, "server running")
})
return nil

}

type Server struct { Logger *slog.Logger logger:"" }

func (s *Server) OnInit(ctx gs.Context) error {

props := ctx.(gs.Container).Properties()

http.HandleFunc("/server/status/open", func(writer http.ResponseWriter, request *http.Request) {
	props.Set("server.open", "true")
	s.Logger.Info("server opened")
})

http.HandleFunc("/server/status/close", func(writer http.ResponseWriter, request *http.Request) {
	props.Set("server.open", "false")
	s.Logger.Info("server closed")
})

go func() {
	http.ListenAndServe(":7878", nil)
}()

return nil

}

func main() {

gs.Object(new(Handler))
gs.Object(new(Server))

if err := gs.Run(); nil != err {
	panic(err)
}

}

// Output: // // $ curl http://127.0.0.1:7878/server/status // server running // // $ curl http://127.0.0.1:7878/server/status/close // // $ curl http://127.0.0.1:7878/server/status // server closed // // $ curl http://127.0.0.1:7878/server/status/open // // $ curl http://127.0.0.1:7878/server/status // server running

参数校验器例子

Go-Spring允许您注册自定义值验证器。如果值验证失败,则Go-Spring将在启动阶段报告一个错误。

在这例子中, 我们将使用 go-validator/validator, 您可以参考这个示例来注册您的自定义验证器。

package main

import ( “fmt” “log/slog”

"github.com/go-spring-projects/go-spring/conf"
"github.com/go-spring-projects/go-spring/gs"
"gopkg.in/validator.v2"

)

const validatorTagName = “validate”

type validatorWrapper struct { validator *validator.Validator }

func (v *validatorWrapper) Field(tag string, i interface{}) error { if 0 == len(tag) { return nil } return v.validator.Valid(i, tag) }

func init() { conf.Register(validatorTagName, &validatorWrapper{validator: validator.NewValidator().WithTag(validatorTagName)}) }

//--------------------------------------------------------------------------

type DBOptions struct { UserName string value:"${username}" Password string value:"${password}" IP string value:"${ip}" Port int32 value:"${port}" validate:"min=1024,max=65535" DB string value:"${db}" validate:"nonzero" }

type MysqlDatabase struct { Logger *slog.Logger logger:"" Options DBOptions value:"${db}" }

func (md *MysqlDatabase) OnInit(ctx gs.Context) error { md.Logger.Info(“mysql connection summary”, “url”, fmt.Sprintf(“mysql://%s:%s@%s:%d/%s”, md.Options.UserName, md.Options.Password, md.Options.IP, md.Options.Port, md.Options.DB)) return nil }

func main() {

gs.Property("db.username", "admin")
gs.Property("db.password", "123456")
gs.Property("db.ip", "127.0.0.1")
gs.Property("db.port", "0") // set db.port=0
gs.Property("db.db", "test")

gs.Object(new(MysqlDatabase))

if err := gs.Run(); nil != err {
	panic(err)
}

}

// // Output: // panic: container refresh failed // ↳object bean “main/main.MysqlDatabase:MysqlDatabase” /projects/go-project/gocase/validator/main.go:58 // ↳bind MysqlDatabase.Options error: validate MysqlDatabase.Options.Port error: less than min

更多特性请参考说明: go-spring-projects/go-spring

最后还要感谢原作者lvan100的辛勤付出。


Golang Go语言-Spring 轻装上阵,重新出发 v1.0.0

更多关于Golang Go语言-Spring 轻装上阵,重新出发 v1.0.0的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

101 回复

Go,Spring,轻装上阵,这三个词同时出现有点难绷……

更多关于Golang Go语言-Spring 轻装上阵,重新出发 v1.0.0的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


讲道理, 只说 spring 是轻量的, 当初 spring 开发出来就是为了取代重量级框架 EJB

为啥要整这么复杂? gin 的路由不好用吗? go 又没有注解,你是不是还得自己加个依赖注入啥的?可别整这么一大坨了,就简单写几个 func 处理逻辑挺好

你这项目名看上去就不轻装

相对原版 go-spring 确实是轻装了许多,精简了很多,只保留了核心的依赖注入相关的东西,而这部分代码是很轻量级的,也才几千行 😀

哈哈 因为确实从 Java Spring 启发而来的 😂

简单的项目确实不需要它,直接撸就行,当你的项目有一定规模的时候,这个时候维护依赖初始化,以及各种属性初始化,就会变得繁琐,也许为了加一个配置参数,要修改很多相关的多个接口

这东西应该只对 java 转 go 的友好但也是一种限制
毕竟 原生写 go 的人应该都不习惯 java 这一套
所以用习惯了自己觉得会 go 了 可能一看别人代码还是会懵 毕竟实现思路不一样

啊这,我还是用 spring 吧,轻量级项目用 gin 甚至不用框架都搞定了。

g…g… gava?

可以改名成 go-summer
简称还是 gs

是的,我开始接触到依赖注入这个概念的时候,大呼魔法啊,怎么加个注解对象就有了,后来一顿摸索发现这个东西真的是写业务的神器啊,可以全身贯注到业务上去而不必费心依赖/配置怎么传,或者初始化来

写 Go 不都是奔着 轻快 去的的么…来个 spring 怎么说呢 应该会有意见吧

是的 Java 里面有很多好东西,比如 netty 的流水线思想,所以我也搞了一个 go-netty ,https://github.com/go-netty/go-netty 😂

不矛盾啊,Go 也有写大规模项目的需求,小规模的项目自己随便弄都行,上了规模它的好处就显现出来了

真香警告 😀

还是你逗 😂,取名真的很困难,这不是为了沾 Java 的光😂

是写一个小功能至少要写三个类的啰嗦写法吗(没写过 java 不太清楚,但是我同事 java 转 go 就是这么啰嗦。。

有没有可能 go 不需要依赖注入。。。

哈哈哈哈哈,为啥不继续写 Java ?

这。。。啊。。。大道至简。。。

要是我写的项目用这玩意,我直接给他删库了。

是的你说的没错,简单的东西确实没有必要,过度抽象也是没有必要的,还是要合理运用

萝卜白菜各有所需嘛

我真不是 Java 转过来的,纯就是馋 Java 的生态,但是又不想入 Java 的坑 😂

萝卜白菜各有所爱,也许有人也喜欢呢 😀

哈哈,不想入坑 Java 所以就整这么个东西

看名字 就感觉不是很轻装

核心代码就几千行,不是 Java 那种以万行计数的庞然大物哈

Javaer 又来嚯嚯其他语言了

Javaer 又来嚯嚯其他语言了
照搬 Java 那套 明显违背了 Go 哲学 Kiss 原则

关键是 go 实现依赖注入很简单, 不少人或公司可以自己实现了,你除非有什么特别之处很难流行。
社区中类似 spring 的 是 uber 的 fx 。
这个更好,直接让 aop 和 gin 集成
https://github.com/shenyisyn/goft-gin

我还真不是 Javaer 转过来的,Go 老大一开始还说不要泛型呢,扭扭咧咧还不是出来了,Spring 确实有很多值得学习的东西,同样的还有 netty ,其流水线装配设计,也是它能一统 Java 网络库江山的核心吸引力之一

是的,uber fx ,google wire ,等众多依赖注入方案都有自己的特点,这也是提供更多的选择给 Gopher 们,毕竟萝卜白菜各有所爱 😀

现在的 spring 已经超过当初的 EJB 多少倍了,哪里还有轻这个概念,IoC 也不是什么好事儿,严重降低代码的可阅读性。

函数式编程已经抛弃了 OOP 的封装,lz 又当成个宝捡回来。。。

我目前简单用用就用的是这个, 我自己还改过一些代码
https://github.com/shenyisyn/goft-ioc

一个疑问?既然要这么做为啥不用 java 去开发呢😅

我老大说过,写业务代码用这个其实就是用来方便控制人的。spring 让写代码的变得没有技术含量, 只关心业务。这个在 go 主力的中间件和云原生领域不好使。

我同意你说的降低代码可读性,鱼和熊掌确实很难同时得到,至于函数式/OOP 之争也不是这几年才有的,虽然但是就个人开发体验来说还是挺好用的,至于路线之争也不会马上就出结果不是吗

我先赞一个,后来学习一下~

万物皆可 spring ,等 PHP-spring ,Python-spring ,……

哥,求求你们这群 java 别来卷 Go 啊,因为我真的是做 Go 的啊 wuwuwu

我就想到点下班怎么就这么难啊 doge

也许大部分的程序员工作内容也没有太多技术含量,这部分始终还是需要人去做不是吗,那我的工作就有意义了,go-spring 确实不是为中间件,基础库准备的,它是纯粹的业务框架,用来给堆业务代码的人提供方便。

wire 是代码是生成的方式,go-spring 是基于反射的方式,支持更多丰富的特性

我不是 Javaer ,我是纯正 Gopher ,go-spring 就是为了让大家早点下班才推出的啊 😂

有没有可能他们不叫 XX-spring 但是也实现了 spring 的思想呢

我写过 2 年多 java ,现在写了 3 年多 go 。go 语言本身不支持注解那一套东西,go 的设计理念也不推崇 spring 那一套。

强行实现 java 那一套,我觉得纯粹是影响性能,恶心后续接手的人。

java 写了 5 年,go2 年 体验下来就是我再也不想写 spring 那套东西 , 代码这东西啊简单直观永远是最优解

google wire 和 uber dig 按 XP 选一个就行

go-spring 这一套机制只工作在初始化层,不至于影响性能

但是不得不承认确实也带来很多方便,不然你看现在很多 javaer 离开 spring 都不知道怎么写程序了😂

要是他俩都看不上眼,也可以来瞧瞧 go-spring 哈,万一看对眼了呢 😀

大项目中,初始化性能也跟重要,假设你十多个集群,每个集群几百台机器,启动慢的话部署一次要老命

> 多年前曾经梦想着能有一天能在 Go 语言中使用上 Spring 那样强大的框架

你的梦想很恐怖

我这边启动速度以几十毫秒计,主要慢得地方主要是注册服务到 zk 里面比较慢

至少现在还是美梦,也许以后会变,谁知道呢

还好我 go 转 rust 了,领先一个版本,不然被卷进去了(玩笑

反射式注入。反射一时爽,性能火葬场

只有初始化阶段是使用反射的

从入门到放弃 3 次了,每次都是在异步一块就看不下去了😂

你找一些 futrure 展开的样子看看就明白了,runtime 本质就是个事件驱动模型,tokio 下面是 mio ,和 c libuv libevent 啥的如出一辙,直接入就完了

不要再嚯嚯我们 Java 码农了,我们真不会试图把 Spring 带去别的社区

我要笑死了这个 😂

多谢哈,隔一段时间我还会重新去捡一遍,期待我的第四轮😀

虽然但是写业务是真香啊

netty 是因为原生 IO 库写的垃圾

怎么名字听上去不怎么轻量,哈哈

老实交代,README 是不是英文写了然后机翻中文的(笑

都用 go 了还搞什么 spring?

语言一般只提供底层接口,上层还是要自己做的,netty 不仅仅解决了原生接口的 bug ,也提供了应用层封装,这才使得网络开发变得容易

这名字就是为了沾光 😂

还真是,有空我去润色一下

主要是依赖注入这块,不仅仅是 spring 独有的,go 里面也有 uber fx, google wire 都是依赖注入思想的实现

去看了一下代码,看到在 go 文件中 NewBean()真的是蚌埠住了,写 java 不好吗

java 恨不得把什么预言都写成一股浓浓の java 味道

没有注解确实是用起来不像 Java 那么丝滑,虽然但是我还是喜欢写 Go 😄

那你用过 go sdk 自带 java 库 还不够好用吗?

我也不喜欢过度抽象,太琐碎了,go-spring 只是解决了依赖注入,不是一定要按照 Java 那种层层封装的风格去写代码

我觉得还可以 go-winter ,让每个人感受寒冬

啥,go sdk 自带 Java 库?真没用过

别来卷 go 了,

我们有套用 java spring 写的业务,还在招人用 go 重写一遍呢……https://v2ex.com/t/987054

别背着臭不可闻的 Java 屎山来污染 Go 了
真的,有空好好研究一下 Go
Spring 码农自以为得意的所谓“框架”,在 Go 面前就像个 200 斤涂着腮红的肥佬

要不然,你们去祸害 php ,整出一个 Php-Spring ?

golang 没有装饰器,全写在 tag 里好变态呀……

我真害怕你明天把 tomcat 拿 go 实现一遍。

语言跟语言是不一样的,Go 是自带单例的,喜欢依赖注入方便测试就多做点 interface ,封装点 struct 然后统一注入进去。

拿框架去实现这种东西是要有性能成本的,反射都是要花 CPU 的,换语言不是把原来的思想原封不动换个语言写,你要适应新的编程思想。

快进到 js-spring. 噢, 原来有 nest.js 啦, 那没事了 /dog

等一个 rust-spring

别这样,你们给楼主打击到了😂支持社区开花,不管是什么花

symfony 就是 php-spring

针对帖子“Golang Go语言-Spring 轻装上阵,重新出发 v1.0.0”,作为IT领域GO语言方面的专家,我给出以下回复:

Go-Spring是基于IoC的Go后端一站式开发框架,为Go开发者提供了一套高度抽象且易于使用的工具集。其v1.0.0版本的发布,标志着Go-Spring以全新的姿态轻装上阵,为Go语言的世界带来了更多的可能性。

Go-Spring深受Java Spring的影响,致力于让Go语言的开发也能享受到Spring框架般的便捷与高效。它提供了依赖注入、控制反转和面向切面编程等核心功能,帮助开发者以更优雅的方式构建和管理GoLang应用程序。

此外,Go-Spring还具备高性能、高并发、简单易用、灵活性强和安全性高等特点。它遵循最小依赖原则,减少了因依赖引发的问题,同时引入了创新功能,如流量录制和回放技术,进一步简化了测试流程,提高了测试覆盖率。

总之,Go-Spring v1.0.0版本的发布是Go语言领域的一次重要事件。它为我们提供了一个强大的工具,让我们能够以更高效、更优雅的方式构建GoLang应用程序。期待Go-Spring在未来的发展中能够持续创新,为Go语言的世界带来更多惊喜。

回到顶部