- Go-Spring 学习笔记一【v1.1.0版本】_一条有梦想的咸鱼`的博客-CSDN博客
- Go-Spring 学习笔记二【v1.1.0版本】_一条有梦想的咸鱼`的博客-CSDN博客
- Go-Spring 学习笔记三【v1.1.0版本】_一条有梦想的咸鱼`的博客-CSDN博客
- Go-Spring 学习笔记四【v1.1.0版本】_一条有梦想的咸鱼`的博客-CSDN博客
Go-Spring 学习笔记一
记录版本
- Go-Spring v1.1.0-rc2版本,项目地址:https://github.com/go-spring/go-spring
安装
# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2
最小化Web应用
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
// 这里必须引入web starter, 也可改为 "github.com/go-spring/starter-echo"
_ "github.com/go-spring/starter-gin"
"log"
)
func init() {
// 创建一个GET方法的访问端点/hello-go-spring, 同Java SpringBoot 的 @GetMapping 注解
// 访问http://127.0.0.1:8080/hello-go-spring 将看到返回{"code": 200, "msg": "SUCCESS", "data": "Hello Go-Spring!"}
gs.GetMapping("/hello-go-spring", func(ctx web.Context) {
// 回调形式返回结果
ctx.JSON(web.SUCCESS.Data("Hello Go-Spring!"))
})
}
func main() {
// 启动应用程序, 同Java SpringBoot 的 SpringApplication.run(...)
log.Fatal(gs.Run())
}
Go-Spring常用语法
1. 对象注入spring bean
package main
import (
"fmt"
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/gs/cond"
"log"
)
type testA struct {
path string `value:"${GOPATH}"`
}
func (t *testA) String() string {
return "testA: " + t.path
}
type testB struct {
a *testA
}
func (t *testB) String() string {
return "testB: " + t.a.String()
}
func newTestB(a *testA) *testB {
return &testB{a: a}
}
func init() {
// 创建bean, 并将bean设置为primary
// Primary() 方法 => 将bean设置为该类型的默认bean
gs.Object(new(testA)).Primary().Init(func(a *testA) {
fmt.Println("1." + a.String())
})
// gs.Provide方法将对象注入spring bean中, Provide方法与Object方法相似, Provide方法还支持方法创建bean的形式, 参见示例6
// Name(string) 方法 => 更改bean的名称
gs.Provide(new(testA)).Name("testA2").Init(func(a *testA) {
a.path = "newPath"
fmt.Println("2." + a.String())
})
// 创建bean并绑定销毁方法
// Destroy(func(interface{})) => 添加bean销毁时的方法
gs.Object(new(testA)).Name("testA3").Destroy(func(a *testA) {
// 程序停止时将打印该内容
fmt.Println("destroy 3." + a.String())
}).Init(func(a *testA) {
fmt.Println("3." + a.String())
})
// 指定创建bean的条件, 与Java Spring @Condition系列注解相同
// On(cond.Condition) => 添加创建bean的条件. 多个条件使用 cond.OnBean("bean1").OnBean("bean2").OnProperty(..) 方式创建
gs.Object(new(testA)).Name("testA4").On(cond.OnBean("testA3")).Init(func(a *testA) {
fmt.Println("4." + a.String())
})
// 设置bean的排序顺序, 优先级从小到大, 同Java Spring @Order注解
// Order(int) => 设置bean的排序顺序
gs.Object(new(testA)).Name("testA5").Order(1).Init(func(a *testA) {
fmt.Println("5." + a.String())
})
// 将方法返回的对象注入至spring bean中, 参数从spring容器中获取
// 当有多个依赖类型且无Primary时, 需指定bean名称,例如: gs.Privide(newTestB, "testA2")...
gs.Provide(newTestB).Init(func(b *testB) {
fmt.Println("6." + b.String())
})
}
func main() {
log.Fatal(gs.Run())
}
2.创建属性监听器
package main
import (
"fmt"
"github.com/go-spring/spring-core/gs"
"log"
)
type user struct {
// 需与配置文件字段名相同
Name string `value:"${name}"`
Age int `value:"${age}"`
}
func init() {
// 创建属性监听器, 当属性装载后会执行自定义方法, 方法签名 func(interface{})
gs.OnProperty("GOPATH", func(s string) {
fmt.Println("4." + s)
})
// 设置环境变量
gs.Setenv("user.name", "dragons")
gs.Setenv("user.age", "24")
// 设置属性(与环境变量的区别在于env是进程全局生效, 而Property仅在一个web容器中生效)
gs.Property("xxx", "value")
// 创建属性监控器, 属性绑定struct, struct的value需与属性名一致
gs.OnProperty("user", func(u user) {
fmt.Printf("5.%v\n", u)
})
/*
创建属性监控器, 数组结构
config/application.yml
users:
- name: dragons
age: 24
- name: xxj
age: 16
*/
gs.OnProperty("users", func(u []user) {
fmt.Printf("6.%v\n", u)
})
}
func main() {
log.Fatal(gs.Run())
}
Web应用常用语法
1.创建Web端点(Path)
1.1 Mapping方式
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
// 引入web引擎starter
_ "github.com/go-spring/starter-gin"
"log"
)
func init() {
// gs.*Mapping(path, func(web.Context))
// GET 请求
gs.GetMapping("/get", func(ctx web.Context) {
// 获取参数-------------------------------------
// 获取请求头参数
ctx.GetHeader("user-agent")
// 获取query参数
ctx.QueryParam("")
// 获取raw参数
_, _ = ctx.GetRawData()
// 返回结果-------------------------------------
// 返回json结构对象或map
/**
{"code": 200, "msg": "SUCCESS"}
*/
ctx.JSON(web.SUCCESS)
ctx.JSON(map[string]interface{}{"code": 200, "msg": "SUCCESS"})
ctx.JSON(struct {
Code int `json:"code"`
Msg string `json:"msg"`
}{200, "SUCCESS"})
// 返回原始串
/* message */
ctx.String("message")
// 返回文件内容
/**
name: dragons
age: 24
*/
ctx.File("./config/application.yml")
})
// POST 请求
gs.PostMapping("/post", func(ctx web.Context) {
// ctx方法与GET请求一致
// 获取表单数据 from-data
ctx.FormValue("name")
})
// PUT 请求
gs.PutMapping("/put", func(ctx web.Context) {
})
// DELETE 请求
gs.DeleteMapping("/delete", func(ctx web.Context) {
})
}
func main() {
log.Fatal(gs.Run())
}
1.2 Binding方式
package main
import (
"context"
"github.com/go-spring/spring-core/gs"
// 引入web引擎starter
_ "github.com/go-spring/starter-gin"
"log"
)
// 示例Bind请求类型
type req struct {
/**
常用tags:
json => 输入、输出 json 结构时的参数名称
xml => 输入、输出 xml 结构时的参数名称
bson => 输入、输出 bson 结构时的参数名称
yaml => 输入、输出 yaml 结构时的参数名称
form => query、form-data 参数名称
...
*/
Name string `json:"name" form:"name" xml:"name" yaml:"name" bson:"name"`
}
// 示例Bind返回类型
type resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data string `json:"data"`
}
func init() {
// gs.*Binding(path, func(context.Context, interface{}) interface{})
// GET 请求
// 其中Binding的fn参数必须是func且只能有两个参数一个返回值, 第一个参数是context.Context而不是web.Context, 第二个参数返回值必须为指针类型
gs.GetBinding("/get", func(ctx context.Context, r *req) *resp {
return &resp{200, "请求成功", r.Name}
})
// POST 请求
gs.PostBinding("/post", func(ctx context.Context, r *req) *resp {
return &resp{200, "请求成功", r.Name}
})
// PUT 请求
gs.PutBinding("/put", func(ctx context.Context, r *req) *resp {
return &resp{200, "请求成功", r.Name}
})
// DELETE请求
gs.DeleteBinding("/delete", func(ctx context.Context, r *req) *resp {
return &resp{200, "请求成功", r.Name}
})
}
func main() {
log.Fatal(gs.Run())
}
1.3 Handle方式
package main
import (
"github.com/go-spring/spring-base/util"
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
// 引入web引擎starter
_ "github.com/go-spring/starter-gin"
"log"
)
// 测试handler, 实现web.Handler接口
type testHandler struct {
}
func (t *testHandler) Invoke(ctx web.Context) {
ctx.JSON(web.SUCCESS)
}
func (t *testHandler) FileLine() (file string, line int, fnName string) {
return util.FileLine(t.Invoke)
}
func init() {
// GET 请求
// 传入一个实现web.Handler接口的对象
// 与 gs.*Mapping区别在于web.Handler接口多了一个FileLine方法, 用于获取用户函数的文件名、行号以及函数名称
gs.HandleGet("/get", &testHandler{})
// POST 请求
gs.HandlePost("/post", &testHandler{})
// PUT 请求
gs.HandlePut("/put", &testHandler{})
// DELETE 请求
gs.HandleDelete("/delete", &testHandler{})
}
func main() {
log.Fatal(gs.Run())
}
配置应用
多环境配置(以yml文件为例)
1. application-dev.yml
user:
name: dragons
age: 24
2. application-test.yml
user:
name: xxl
age: 14
3. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
"log"
)
type user struct {
Name string `value:"${name}"`
Age int `value:"${age}"`
}
func init() {
// 设置配置目录
gs.Setenv("GS_SPRING_CONFIG_LOCATIONS", "config/")
// 设置dev环境配置值, 两种方式均可
gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev")
// gs.Setenv("spring.profiles.active", "dev")
gs.OnProperty("user", func(u user) {
gs.GetMapping("/user", func(ctx web.Context) {
ctx.JSON(web.SUCCESS.Data(u))
})
})
}
func main() {
log.Fatal(gs.Run())
}
配置方式一:Tag value + autowire注入
1. config/application.yml
# 默认情况下自动加载项目路径下的 conifg/application.yml 或 config/application.properties 文件配置
# 这里以application.yml为例, config/application.yml 文件
name: dragons
age: 24
2. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
"log"
)
// 定义model
type user struct {
// 加载环境变量中的 name 属性, ":=" 语法用于设置默认值, 同Java Spring @Value("${name:}")
Name string `json:"name" value:"${name:=}"`
// 加载环境变量中的 age 属性
Age int `json:"age" value:"${age:=0}"`
}
// 定义控制器
type userController struct {
// 自动注入*user实例, 也可将?改为具体beanName, 例如当前测试 ? 可改为 user
u *user `autowire:"?"`
}
// 定义控制器端点方法, 控制器端点方法签名为 func(..) ...(ctx web.Context)
func (c *userController) echoUser(ctx web.Context) {
// 这里直接返回model对象, 指针类型与值类型最终都将转为值类型
ctx.JSON(c.u)
}
func init() {
// 创建user类型的bean实例, 自动装载user配置
gs.Object(new(user))
// 创建userController控制器bean实例, 并在控制器实例创建后添加初始化方法
// 访问http://127.0.0.1:8080/echo-user 将看到返回{"name": "dragons", "age": 24}
gs.Object(new(userController)).Init(func(c *userController) {
// 初始化web端点/echo-user, 将该端点绑定至userController控制器的echoUser方法上
gs.GetMapping("/echo-user", c.echoUser)
})
}
func main() {
log.Fatal(gs.Run())
}
配置方式二: Tag value 属性直接注入
1.config/application.yml
users:
- name: dragons
age: 24
- name: xxy
age: 14
2. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
"log"
)
// 定义model
type user struct {
// 设置tag value对应的配置名, users下的属性名为name, 因此这里填写${name}即可注入
Name string `json:"name" form:"name" yaml:"name" value:"${name}"`
// 设置tag value对应的配置名
Age int `json:"age" form:"age" yaml:"age" value:"${age}"`
}
// 定义controller
type userController struct {
// 使用application.yml中的users属性直接注入
users []user `value:"${users}"`
}
// 控制器端点方法
func (c *userController) echoUsers(ctx web.Context) {
ctx.JSON(c.users)
}
func init() {
// 创建userController控制器bean实例, 并在控制器实例创建后添加初始化方法
gs.Object(new(userController)).Init(func(controller * userController) {
// 初始化web端点/echo-users, 将该端点绑定至userController控制器的echoUser方法上
gs.GetMapping("/echo-users", controller.echoUsers)
})
}
func main() {
log.Fatal(gs.Run())
}
Go-Spring 学习笔记二
记录版本
- Go-Spring v1.1.0-rc2,项目地址:https://github.com/go-spring/go-spring
安装
# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2
# 安装gorm starter
go get -u github.com/go-spring/starter-gorm@v1.1.0-rc2
# 安装gorm
go get -u github.com/jinzhu/gorm
# 安装 redis starter
# go-redis
go get -u github.com/go-spring/starter-go-redis@v1.1.0-rc2
# redigo
go get -i github.com/go-spring-starter-redigo@v1.1.0-rc2
Web 应用集成starter-gorm
1. 配置文件config/application.yml
# gorm starter mysql 默认配置项, 同spring.datasource
db:
url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local
2. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
// 需要引入该模块自动装载默认的gorm, 若无需使用自动装载可自定义gorm bean
_ "github.com/go-spring/starter-gorm/mysql"
"github.com/jinzhu/gorm"
"log"
)
// 定义model
/**
建表sql
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1460907264817729538 DEFAULT CHARSET=utf8
数据sql
INSERT INTO `user` VALUES(1, "dragons", 24, "521274311@qq.com")
*/
type user struct {
// 主键ID
Id int64 `json:"id" gorm:"primary_key"`
Username string `json:"username"`
Age int `json:"age"`
Email string `json:"email"`
}
// TableName 获取表名方法, 默认复数表名, 这里表名与model名称相同需要实现Tabler接口
func (u *user) TableName() string {
return "user"
}
// 定义控制器
type gormController struct {
// 自动注入starter启动的*gorm.DB
db *gorm.DB `autowire:"?"`
}
// 定义查询所有用户的端点方法
func (c *gormController) selectUsers(ctx web.Context) {
var users []user
// 查询所有用户信息
c.db.Model(&user{}).Find(&users)
// 返回所有用户信息
ctx.JSON(&users)
}
func init() {
// 创建 gormController 控制器bean实例, 并在控制器实例创建后添加初始化方法
// 访问 http://127.0.0.1:8080/users 将看到返回 [{"id": 1, "username", "dragons", "age": 24, "email": "521274311@qq.com"}]
gs.Object(new(gormController)).Init(func(c *gormController) {
// 添加Web端点/users, 绑定gormController.selectUsers方法
gs.GetMapping("/users", c.selectUsers)
})
}
func main() {
log.Fatal(gs.Run())
}
Web 应用集成gorm多数据源
1.config/application.yml
# 这里自定义了一个配置项 dbs (可自行更改) 来配置多数据源, 其中name为bean名称, type为数据库类型(与gorm数据库类型名称一致)
dbs:
- name: gorm-db1
url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local
type: mysql
- name: gorm-db2
url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local
type: mysql
2. 创建一个dbs.go文件用来单独注入*gorm.DB实例
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/jinzhu/gorm"
"github.com/labstack/gommon/log"
// 需要引入Mysql驱动
_ "github.com/jinzhu/gorm/dialects/mysql"
)
// 定义配置struct, 属性与配置文件相同
type DBConfig struct {
Name string `value:"${name}"`
Url string `value:"${url}"`
Type string `value:"${type}"`
}
func init() {
// 创建一个配置监听器, 当配置加载后将执行该方法
gs.OnProperty("dbs", func(config []DBConfig) {
// 遍历配置项
for _, dbConfig := range config {
db, err := CreateDB(dbConfig)
if err != nil {
log.Fatal(err)
}
// 将*gorm.DB注入Spring bean中
gs.Object(db).Destroy(CloseDB).Name(dbConfig.Name)
}
})
}
func CreateDB(config DBConfig) (*gorm.DB, error) {
return gorm.Open(config.Type, config.Url)
}
func CloseDB(db *gorm.DB) {
log.Info("close gorm mysql")
if err := db.Close(); err != nil {
log.Error(err)
}
}
3. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
"github.com/jinzhu/gorm"
"log"
)
// 定义model
/**
建表sql
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1460907264817729538 DEFAULT CHARSET=utf8
数据sql
INSERT INTO `user` VALUES(1, "dragons", 24, "521274311@qq.com")
*/
type user struct {
// 主键ID
Id int64 `json:"id" gorm:"primary_key"`
Username string `json:"username"`
Age int `json:"age"`
Email string `json:"email"`
}
// TableName 获取表名方法, 默认复数表名, 这里表名与model名称相同需要实现Tabler接口
func (u *user) TableName() string {
return "user"
}
// 定义控制器
type gormController struct {
// 注入指定bean名称为gorm-db1的实例
/*
v1.1.0移除getBean方法, 属性注入阶段后不保留研数据, 若需多项db切换则需要提前注入多项db的bean,自行实现切换逻, 示例如下:
type StructName struct {
db1 *gorm.DB `autowire:"gorm-db1"`
db2 *gorm.DB `autowire:"gorm-db2"`
}
*/
db *gorm.DB `autowire:"gorm-db1"`
}
// 定义查询所有用户的端点方法
func (c *gormController) selectUsers(ctx web.Context) {
var users []user
// 查询所有用户信息
c.db.Model(&user{}).Find(&users)
// 返回所有用户信息
ctx.JSON(&users)
}
func init() {
// 创建 gormController 控制器bean实例, 并在控制器实例创建后添加初始化方法
// 访问http://127.0.0.1:8080/users 将看到返回[{"id": 1, "username", "dragons", "age": 24, "email": "521274311@qq.com"}]
gs.Object(new(gormController)).Init(func(c *gormController) {
// 添加Web端点/users, 绑定gormController.selectUsers方法
gs.GetMapping("/users", c.selectUsers)
})
}
func main() {
log.Fatal(gs.Run())
}
Web 应用集成starter-go-redis/starter-redigo
1. config/application.yml
# redis starter 配置项
redis:
host: host
port: 6379
password: password
database: 0
2. main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/redis"
"github.com/go-spring/spring-core/web"
_ "github.com/go-spring/starter-gin"
"log"
// 引入go-redis starter依赖
_ "github.com/go-spring/starter-go-redis"
// 引入redigo starter依赖
// _ "github.com/go-spring/starter-redigo"
)
// 定义 redisController 控制器
type redisController struct {
// 注入redis.Client bean, 注意redis为github.com/go-spring/spring-core/redis
redisClient redis.Client `autowire:"?"`
}
// redis get 方法
func (c *redisController) get(ctx web.Context) {
// 获取入参的key
key := ctx.QueryParam("key")
// redis get [key] 获取value
value, err := c.redisClient.Get(ctx.Context(), key)
if err != nil {
ctx.JSON(web.ERROR.Error(err))
} else {
ctx.JSON(web.SUCCESS.Data(value))
}
}
func init() {
gs.Object(new(redisController)).Init(func(c *redisController) {
gs.GetMapping("/redis/get", c.get)
})
}
func main() {
log.Fatal(gs.Run())
}
Go-Spring 学习笔记三
记录版本
- Go-Spring v1.1.0-rc2
安装
# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2
1.过滤器
1.1 创建Web过滤器
main.go
package main
import (
"errors"
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
// 引入spring web引擎
// gin
SpringGin "github.com/go-spring/spring-gin"
// echo
//SpringEcho "github.com/go-spring/spring-echo"
// 这里使用SpringGin方式创建一个web容器, 因此引入web starter而不是具体某个web引擎的starter
_ "github.com/go-spring/starter-web"
"log"
)
// 定义token过滤器
type TokenFilter struct {
}
/**
设置过滤器指定的path pattern范围, 这里设置为 /test2/* 即拦截 /test2/*类型的端点
若不定义该方法则默认为全局拦截器
*/
func (f *TokenFilter) URLPatterns() []string {
return []string{"/test2/*"}
}
// 实现 web.Filter 接口
func (t *TokenFilter) Invoke(ctx web.Context, chain web.FilterChain) {
token := ctx.QueryParam("token")
if token != "abcde" {
ctx.JSON(web.ERROR.Error(errors.New("invalid token")))
} else {
chain.Next(ctx)
}
}
func init() {
// 使用SpringGin创建web容器, 使用该方式可创建多http server
webContainer := SpringGin.NewContainer(web.ContainerConfig{
Port: 8080,
BasePath: "/",
})
// 为web容器添加token过滤器
webContainer.AddFilter(&TokenFilter{})
// 若直接使用默认容器则使用如下方法添加过滤器
// gs.Object(&TokenFilter{}).Export((*web.Filter)(nil))
// 注入容器并设置导出接口
gs.Object(webContainer).Export((*web.Container)(nil))
/*
访问 http://127.0.0.1:8080/test/t1 将不会经过token拦截器, 返回:
{
"code": 200,
"msg": "SUCCESS"
}
访问 http://127.0.0.1:8080/test2/t1 将被token过滤器拦截, 返回:
{
"code": -1,
"msg": "ERROR",
"err": "/path/main.go:32: invalid userId"
}
访问 http://127.0.0.1:8080/test2/t1?token=abcde 将被token过滤器放行, 返回:
{
"code": 200,
"msg": "SUCCESS"
}
*/
gs.Object(new(testController)).Init(func(c *testController) {
gs.GetMapping("/test/t1", c.echo)
gs.GetMapping("/test2/t1", c.echo)
})
}
// 定义测试控制器
type testController struct {
}
func (c *testController) echo(ctx web.Context) {
ctx.JSON(web.SUCCESS)
}
func main() {
log.Fatal(gs.Run())
}
在spring-gin v1.1.0-rc2版本中, 中间件不会直接拦截并返回结果, 而是将拦截器的结果与端点方法返回的结果组合返回, 这里修改源码文件github.com/go-spring/spring-gin/container.go的168行与174行即可解决(问题已反馈, 等到正式版应该不会有这种问题了)
// ...省略
func HandlerWrapper(fn web.Handler, wildCardName string, filters []web.Filter) []gin.HandlerFunc {
var handlers []gin.HandlerFunc
handlers = append(handlers, func(ginCtx *gin.Context) {
if WebContext(ginCtx) == nil {
NewContext(fn, wildCardName, ginCtx)
}
})
for _, filter := range filters {
f := filter
handlers = append(handlers, func(ginCtx *gin.Context) {
f.Invoke(WebContext(ginCtx), &ginFilterChain{ginCtx})
ginCtx.Abort() // 168行之后添加该代码
})
}
handlers = append(handlers, func(ginCtx *gin.Context) {
fn.Invoke(WebContext(ginCtx))
ginCtx.Abort() // 174行之后添加该代码
})
return handlers
}
// ...省略
1.2 替换默认Web日志过滤器
Go-Spring启动的Web容器会带有默认的日志过滤器,当我们不需要使用默认日志而自定义日志时,可以在创建容器时重新设置日志过滤器, 如下所示:
main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
SpringGin "github.com/go-spring/spring-gin"
// 仅使用starter web
_ "github.com/go-spring/starter-web"
"log"
"time"
)
func init() {
// 方式一
// 创建gin容器, 并设置初始化方法
gs.Object(SpringGin.NewContainer(web.ContainerConfig{
Port: 8080,
BasePath: "/",
})).Init(func(container *SpringGin.Container) {
// 直接使用内置的web.FuncFilter方法构建过滤器
container.SetLoggerFilter(web.FuncFilter(func(ctx web.Context, chain web.FilterChain) {
// 自定义日志信息
startTime := time.Now()
chain.Next(ctx)
log.Printf("接口耗时:%v", time.Since(startTime))
}))
// 导出为web.Container类型(若不指定将无法检测到web容器)
}).Export((*web.Container)(nil))
// 方式二
// 使用gs.Provide + 函数方式创建 web 容器, 创建容器过程中配置容器日志过滤器及其他信息
//gs.Provide(func() web.Container {
// webContainer := SpringGin.NewContainer(web.ContainerConfig{
// Port: 8080,
// BasePath: "/",
// })
// webContainer.SetLoggerFilter(web.FuncFilter(func(ctx web.Context, chain web.FilterChain) {
// startTime := time.Now()
// chain.Next(ctx)
// log.Printf("接口耗时:%v", time.Since(startTime))
// }))
// return webContainer
//})
// 定义GET方法端点
gs.GetMapping("/", func(ctx web.Context) {
ctx.JSON(web.SUCCESS.Data("Hello Go-Spring!"))
})
}
func main() {
log.Fatal(gs.Run())
}
Go-Spring 学习笔记四
记录版本
- Go-Spring v1.1.0-rc2版本,Git地址:https://github.com/go-spring/go-spring
安装
# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# 安装grpc starter
go get -u github.com/go-spring/starter-grpc@v1.1.0-rc2
# 安装grpc server
go get -u github.com/go-spring/starter-grpc/server@v1.1.0-rc2
# 安装grpc client
go get -u github.com/go-spring/starter-grpc/client@v1.1.0-rc2
使用Grpc通信
1. 工程结构
|-- client
|-- main.go # 客户端启动文件
|-- server
|-- main.go # 服务端启动文件
|-- proto # 存放proto文件目录
|-- product.proto # 测试product grpc服务
|-- pb # 存放生成的go grpc pb文件
|-- product.pb.go # product service 生成的pb文件
|-- service # 服务实现
|-- product.go # product 服务实现
|-- go.mod
...
2. 准备工作
go 开发 grpc 相关配置及依赖参考 https://www.cnblogs.com/baoshu/p/13488106.html,主要安装一个protoc可执行文件与protoc-gen-go依赖来自动生成pb代码
3. Protobuf文件
在 proto目录下创建文件product.proto文件如下
syntax = "proto3";
// 定义go文件生成路径
option go_package = "proto/pb";
// 定义生成service的包名
package product;
// 定义服务
service Product {
// 定义rpc端点
rpc Produce (ProductRequest) returns(ProductResponse) {}
}
// 定义Produce端点的请求参数
message ProductRequest {
// 参数类型与参数名称
string name = 1;
int64 count = 2;
}
// 定义Produce端点的响应参数
message ProductResponse {
// 参数类型与参数名称
uint32 code = 1;
string msg = 2;
string items = 3;
}
4. 生成*.pb.go文件
在工程根目录下执行命令
# protoc我这里已经添加到环境变量了, 若为添加环境变量则需要写出protoc的全路径
protoc --go_out=plugins=grpc:./ ./proto/product.proto
5. Service 实现
在service目录下创建product.go文件
package service
import (
context "context"
// 引入生成的pb文件对应的模块
"xxx/proto/pb"
"strconv"
)
// 定义service 需要实现生成pb文件的ProductServer接口
type ProductService struct {
}
// pb.ProductServer接口实现
func (p *ProductService) Produce(ctx context.Context, request *pb.ProductRequest) (*pb.ProductResponse, error) {
return &pb.ProductResponse{
Code: 200,
Msg: "请求成功",
Items: "Produced " + strconv.FormatInt(request.Count, 10) + " product named \"" + request.Name + "\"",
}, nil
}
6. 服务端启动程序
server/main.go
package main
import (
"github.com/go-spring/spring-base/log"
"github.com/go-spring/spring-core/grpc"
"github.com/go-spring/spring-core/gs"
// 引入grpc server starter
_ "github.com/go-spring/starter-grpc/server"
"xxx/proto/pb"
"xxx/service"
)
func init() {
// 创建service.ProductService bean示例并设置初始方法
gs.Object(new(service.ProductService)).Init(func(srv *service.ProductService) {
// 添加grpc服务
// gs.GrpcServer(string, *grpc.Server) 其中第一个参数为serviceName, 要求与生成的pb文件Service.Desc中的ServiceName参数值相等
gs.GrpcServer("product.Product", &grpc.Server{
// 服务注册方法
Register: pb.RegisterProductServer,
// 服务实现对象
Service: srv,
})
})
}
func main() {
// 设置grpc访问端口
gs.Property("grpc.server.port", 8081)
log.Fatal(gs.Run())
}
7. 客户端启动程序
client/main.go
package main
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/spring-core/web"
// 引入gin web starter, 若不需要提供web访问可以移除该依赖
_ "github.com/go-spring/starter-gin"
// 引入 grpc client starter
_ "github.com/go-spring/starter-grpc/client"
"xxx/proto/pb"
"log"
)
func init() {
// 配置product访问端点, 配置结构为 grpc.endpoint.xxx.address 其中xxx为自己定义的名称
gs.Property("grpc.endpoint.product.address", "127.0.0.1:8081")
// 1.gs.GrpcClient(interface{}, string)创建grpc客户端bean实例, 第二个参数的endpoint名称必须与上面引入的名称一致
// 2.Init(..) bean初始化的方法
gs.GrpcClient(pb.NewProductClient, "product").Init(func(client pb.ProductClient) {
// 创建Web端点
gs.GetMapping("/", func(ctx web.Context) {
// 调用rpc服务端实现并获取结果
products, err := client.Produce(ctx.Context(), &pb.ProductRequest{
Name: ctx.QueryParam("name"),
Count: 10,
})
// 结果返回
if err != nil {
ctx.JSON(web.ERROR.Error(err))
} else {
ctx.JSON(products)
}
})
})
}
func main() {
log.Fatal(gs.Run())
}