Go-Spring 轻装上阵,重新出发 v1.0.0 !

2023-11-03 10:25:13 +08:00
 limpo

前言

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

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

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

特性

动态属性例子

允许在运行时动态刷新属性,不仅支持基本数据类型,还支持结构、切片和 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的辛勤付出。

6911 次点击
所在节点    Go 编程语言
111 条回复
lsk569937453
2023-11-03 11:03:25 +08:00
有没有可能 go 不需要依赖注入。。。
TypeErrorNone
2023-11-03 11:03:34 +08:00
哈哈哈哈哈,为啥不继续写 Java ?
Smilencer
2023-11-03 11:04:28 +08:00
这。。。啊。。。大道至简。。。
danbai
2023-11-03 11:04:53 +08:00
要是我写的项目用这玩意,我直接给他删库了。
xingjue
2023-11-03 11:05:49 +08:00
gava 警告?
limpo
2023-11-03 11:06:21 +08:00
@Maboroshii 是的你说的没错,简单的东西确实没有必要,过度抽象也是没有必要的,还是要合理运用
limpo
2023-11-03 11:06:40 +08:00
@lsk569937453 萝卜白菜各有所需嘛
limpo
2023-11-03 11:07:30 +08:00
@TypeErrorNone 我真不是 Java 转过来的,纯就是馋 Java 的生态,但是又不想入 Java 的坑 😂
limpo
2023-11-03 11:08:31 +08:00
@danbai 萝卜白菜各有所爱,也许有人也喜欢呢 😀
limpo
2023-11-03 11:09:53 +08:00
@xingjue 哈哈,不想入坑 Java 所以就整这么个东西
to2false
2023-11-03 11:11:21 +08:00
@danbai +1
tietou
2023-11-03 11:14:03 +08:00
看名字 就感觉不是很轻装
asdfa
2023-11-03 11:16:22 +08:00
🐶🐸
limpo
2023-11-03 11:17:13 +08:00
@tietou 核心代码就几千行,不是 Java 那种以万行计数的庞然大物哈
me1onsoda
2023-11-03 11:17:32 +08:00
Javaer 又来嚯嚯其他语言了
Dogtler
2023-11-03 11:19:40 +08:00
Javaer 又来嚯嚯其他语言了
照搬 Java 那套 明显违背了 Go 哲学 Kiss 原则
mightybruce
2023-11-03 11:24:28 +08:00
关键是 go 实现依赖注入很简单, 不少人或公司可以自己实现了,你除非有什么特别之处很难流行。
社区中类似 spring 的 是 uber 的 fx 。
这个更好,直接让 aop 和 gin 集成
https://github.com/shenyisyn/goft-gin
limpo
2023-11-03 11:24:47 +08:00
@Dogtler @me1onsoda 我还真不是 Javaer 转过来的,Go 老大一开始还说不要泛型呢,扭扭咧咧还不是出来了,Spring 确实有很多值得学习的东西,同样的还有 netty ,其流水线装配设计,也是它能一统 Java 网络库江山的核心吸引力之一
miniliuke
2023-11-03 11:26:38 +08:00
@abcdexx jovo
limpo
2023-11-03 11:27:10 +08:00
@mightybruce 是的,uber fx ,google wire ,等众多依赖注入方案都有自己的特点,这也是提供更多的选择给 Gopher 们,毕竟萝卜白菜各有所爱 😀

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/988146

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX