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

199 天前
 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的辛勤付出。

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

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

https://www.v2ex.com/t/988146

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

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

© 2021 V2EX