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

6915 次点击
所在节点    Go 编程语言
111 条回复
sadfQED2
2023-11-03 12:13:21 +08:00
@limpo 大项目中,初始化性能也跟重要,假设你十多个集群,每个集群几百台机器,启动慢的话部署一次要老命
Leprax
2023-11-03 12:32:28 +08:00
> 多年前曾经梦想着能有一天能在 Go 语言中使用上 Spring 那样强大的框架

你的梦想很恐怖
limpo
2023-11-03 12:39:51 +08:00
@sadfQED2 我这边启动速度以几十毫秒计,主要慢得地方主要是注册服务到 zk 里面比较慢
limpo
2023-11-03 12:40:53 +08:00
@Leprax 至少现在还是美梦,也许以后会变,谁知道呢
nigerunndayo
2023-11-03 12:57:23 +08:00
@limpo 是这样的
kirito41dd
2023-11-03 13:31:23 +08:00
还好我 go 转 rust 了,领先一个版本,不然被卷进去了(玩笑
Rehtt
2023-11-03 13:44:14 +08:00
反射式注入。反射一时爽,性能火葬场
limpo
2023-11-03 13:46:44 +08:00
@Rehtt 只有初始化阶段是使用反射的
limpo
2023-11-03 13:48:02 +08:00
@kirito41dd 从入门到放弃 3 次了,每次都是在异步一块就看不下去了😂
kirito41dd
2023-11-03 13:57:07 +08:00
@limpo 你找一些 futrure 展开的样子看看就明白了,runtime 本质就是个事件驱动模型,tokio 下面是 mio ,和 c libuv libevent 啥的如出一辙,直接入就完了
Miy4mori
2023-11-03 14:04:36 +08:00
不要再嚯嚯我们 Java 码农了,我们真不会试图把 Spring 带去别的社区
qingshui33
2023-11-03 14:05:03 +08:00
@abcdexx 我要笑死了这个 😂
limpo
2023-11-03 14:11:26 +08:00
@kirito41dd 多谢哈,隔一段时间我还会重新去捡一遍,期待我的第四轮😀
limpo
2023-11-03 14:14:29 +08:00
@Miy4mori 虽然但是写业务是真香啊
beidounanxizi
2023-11-03 14:17:15 +08:00
@limpo netty 是因为原生 IO 库写的垃圾
yrzs
2023-11-03 14:25:23 +08:00
怎么名字听上去不怎么轻量,哈哈
SoyaDokio
2023-11-03 14:26:47 +08:00
老实交代,README 是不是英文写了然后机翻中文的(笑
snowlyg
2023-11-03 14:29:08 +08:00
都用 go 了还搞什么 spring?
limpo
2023-11-03 14:29:48 +08:00
@beidounanxizi 语言一般只提供底层接口,上层还是要自己做的,netty 不仅仅解决了原生接口的 bug ,也提供了应用层封装,这才使得网络开发变得容易
limpo
2023-11-03 14:30:29 +08:00
@yrzs 这名字就是为了沾光 😂

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

https://tanronggui.xyz/t/988146

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

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

© 2021 V2EX