​从别人的代码中学习golang系列--02

golang基础学习以及代码实例 这是我亲手整理非常全的go语言基础知识点以及代码实例,对GO有情趣的同学可以通过这个总结以及代码实例快速入门!加油同学们! 阅读详情

这篇博客还是整理从https://github.com/LyricTian/gin-admin 这个项目中学习的golang相关知识

作者在项目中使用了https://github.com/google/wire 做依赖注入,这个库我之前没有使用过,看了作者代码中的使用,至少刚开始是看着有点懵,不知道是做什么,所以这篇博客主要就是整理这个包的使用

依赖注入是什么?

如果你搜索「依赖注入」,百度百科里可能先看到的是「控制反转」,下面是百度百科的解释

❝

「控制反转」(Inversion of Control,缩写为「IoC」),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做「依赖注入」(Dependency Injection,简称「DI」),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

❞

这样的解释可能还是不好理解,所以我们通过一个简单的代码来理解应该就清楚很多。

我们用程序实现:小明对世界说:"hello golang"

这里将小明抽象为People  说的内容抽象为:Message  小明说 hello golang 抽象为:Event, 代码如下:

package main


import "fmt"


var msg = "Hello World!"


func NewMessage() Message {
   return Message(msg)
}


// 要说的内容的抽象
type Message string


func NewPeople(m Message) People {
   return People{name: "小明", message: m}
}


// 小明这个人的抽象
type People struct {
   name    string
   message Message
}


// 小明这个人会说话
func (p People) SayHello() string {
   msg := fmt.Sprintf("%s 对世界说:%s\n", p.name, p.message)
   return msg
}


func NewEvent(p People) Event {
   return Event{people: p}
}


// 小明去说话这个行为抽象为一个事件
type Event struct {
   people People
}


func (e Event) start() {
   msg := e.people.SayHello()
   fmt.Println(msg)
}


func main() {
   message := NewMessage()
   people := NewPeople(message)
   event := NewEvent(people)
   event.start()
}

从上面这个代码我们可以看出,我们必须先初始化一个NewMessage, 因为NewPeople依赖它,NewEvent 依赖NewPeople. 这还是一种比较简单的依赖关系,实际生产的依赖关系可能会更复杂,那么什么好的办法来处理这种依赖,https://github.com/google/wire 就是来干这件事情的。

wire依赖注入例子

栗子1

安装:go get github.com/google/wire/cmd/wire

上面的代码,我们用wire的方式实现,代码如下:

package main


import (
   "fmt"


   "github.com/google/wire"
)


var msg = "Hello World!"


func NewMessage() Message {
   return Message(msg)
}


// 要说的内容的抽象
type Message string


func NewPeople(m Message) People {
   return People{name: "小明", message: m}
}


// 小明这个人的抽象
type People struct {
   name    string
   message Message
}


// 小明这个人会说话
func (p People) SayHello() string {
   msg := fmt.Sprintf("%s 对世界说:%s\n", p.name, p.message)
   return msg
}


func NewEvent(p People) Event {
   return Event{people: p}
}


// 小明去说话这个行为抽象为一个事件
type Event struct {
   people People
}


func (e Event) start() {
   msg := e.people.SayHello()
   fmt.Println(msg)
}


func InitializeEvent() Event {
   wire.Build(NewEvent, NewPeople, NewMessage)
   return Event{}
}


func main() {
   e := InitializeEvent()
   e.start()
}

这里我们不用再手动初始化NewEvent, NewPeople, NewMessage,而是通过需要初始化的函数传递给wire.Build , 这三者的依赖关系,wire 会帮我们处理,我们通过wire . 的方式生成代码:

➜  useWireBaseExample2 wire .
wire: awesomeProject/202006/useWireBaseExample2: wrote /home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample2/wire_gen.go
➜  useWireBaseExample2

会在当前目录下生成wire_gen.go的代码,内容如下:

// Code generated by Wire. DO NOT EDIT.


//go:generate wire
//+build !wireinject


package main


import (
   "fmt"
)


// Injectors from main.go:


func InitializeEvent() Event {
   message := NewMessage()
   people := NewPeople(message)
   event := NewEvent(people)
   return event
}


// main.go:


var msg = "Hello World!"


func NewMessage() Message {
   return Message(msg)
}


// 要说的内容的抽象
type Message string


func NewPeople(m Message) People {
   return People{name: "小明", message: m}
}


// 小明这个人的抽象
type People struct {
   name    string
   message Message
}


// 小明这个人会说话
func (p People) SayHello() string {
   msg2 := fmt.Sprintf("%s 对世界说:%s\n", p.name, p.message)
   return msg2
}


func NewEvent(p People) Event {
   return Event{people: p}
}


// 小明去说话这个行为抽象为一个事件
type Event struct {
   people People
}


func (e Event) start() {
   msg2 := e.people.SayHello()
   fmt.Println(msg2)
}


func main() {
   e := InitializeEvent()
   e.start()
}

代码中wire为我们生成了如下代码:

// Injectors from main.go:


func InitializeEvent() Event {
   message := NewMessage()
   people := NewPeople(message)
   event := NewEvent(people)
   return event
}


在看看我们刚开始写的代码,发现其实是一样的,是不是感觉方便了很多。

「注意:当使用 Wire 时,我们将同时提交 Wire.go 和 Wire _ gen 到代码仓库」

wire 能做的事情很多,如果我们相互依赖的初始化其中有初始化失败的,wire也能帮我们很好的处理。

栗子2

package main


import (
   "errors"
   "fmt"
   "os"
   "time"


   "github.com/google/wire"
)


var msg = "Hello World!"


func NewMessage() Message {
   return Message(msg)
}


// 要说的内容的抽象
type Message string


func NewPeople(m Message) People {
   var grumpy bool
   if time.Now().Unix()%2 == 0 {
      grumpy = true
   }
   return People{name: "小明", message: m, grumpy: grumpy}
}


// 小明这个人的抽象
type People struct {
   name    string
   message Message
   grumpy  bool // 脾气是否暴躁
}


// 小明这个人会说话
func (p People) SayHello() string {
   if p.grumpy {
      // 脾气暴躁,心情不好
      msg := "Go away !"
      return msg
   }
   msg := fmt.Sprintf("%s 对世界说:%s\n", p.name, p.message)
   return msg


}


func NewEvent(p People) (Event, error) {
   if p.grumpy {
      return Event{}, errors.New("could not create event: event greeter is grumpy")
   }
   return Event{people: p}, nil
}


// 小明去说话这个行为抽象为一个事件
type Event struct {
   people People
}


func (e Event) start() {
   msg := e.people.SayHello()
   fmt.Println(msg)
}


func InitializeEvent() (Event, error) {
   wire.Build(NewEvent, NewPeople, NewMessage)
   return Event{}, nil
}


func main() {
   e, err := InitializeEvent()
   if err != nil {
      fmt.Printf("failed to create event: %s\n", err)
      os.Exit(2)
   }
   e.start()
}

更改之后的代码初始化NewEvent 可能就会因为People.grumpy 的值而失败,通过wire生成之后的代码

// Injectors from main.go:


func InitializeEvent() (Event, error) {
   message := NewMessage()
   people := NewPeople(message)
   event, err := NewEvent(people)
   if err != nil {
      return Event{}, err
   }
   return event, nil
}

栗子3

我们再将上面的代码进行更改:

package main


import (
   "errors"
   "fmt"
   "os"
   "time"


   "github.com/google/wire"
)


func NewMessage(msg string) Message {
   return Message(msg)
}


// 要说的内容的抽象
type Message string


func NewPeople(m Message) People {
   var grumpy bool
   if time.Now().Unix()%2 == 0 {
      grumpy = true
   }
   return People{name: "小明", message: m, grumpy: grumpy}
}


// 小明这个人的抽象
type People struct {
   name    string
   message Message
   grumpy  bool // 脾气是否暴躁
}


// 小明这个人会说话
func (p People) SayHello() string {
   if p.grumpy {
      // 脾气暴躁,心情不好
      msg := "Go away !"
      return msg
   }
   msg := fmt.Sprintf("%s 对世界说:%s\n", p.name, p.message)
   return msg


}


func NewEvent(p People) (Event, error) {
   if p.grumpy {
      return Event{}, errors.New("could not create event: event greeter is grumpy")
   }
   return Event{people: p}, nil
}


// 小明去说话这个行为抽象为一个事件
type Event struct {
   people People
}


func (e Event) start() {
   msg := e.people.SayHello()
   fmt.Println(msg)
}


func InitializeEvent(msg string) (Event, error) {
   wire.Build(NewEvent, NewPeople, NewMessage)
   return Event{}, nil
}


func main() {
   msg := "Hello Golang"https://github.com/LyricTian/gin-admin
   e, err := InitializeEvent(msg)
   if err != nil {
      fmt.Printf("failed to create event: %s\n", err)
      os.Exit(2)
   }
   e.start()
}

上面的更改主要是NewPeople 函数增加了msg参数,同时InitializeEvent增加了msg参数,这个时候我们通过wire生成代码则可以看到如下:

// Injectors from main.go:


func InitializeEvent(msg string) (Event, error) {
	message := NewMessage(msg)
	people := NewPeople(message)
	event, err := NewEvent(people)
	if err != nil {
		return Event{}, err
	}
	return event, nil
}

wire 会检查注入器的参数,并检查到NewMessage 需要msg的参数,所以它将msg传递给了NewMessage

栗子4

如果我们传给wire.Build 的依赖关系存在问题,wire会怎么处理呢?我们调整InitializeEvent 的代码:

func InitializeEvent(msg string) (Event, error) {
   wire.Build(NewEvent, NewMessage)
   return Event{}, nil
}

然后执行wire 进行代码的生成:

➜  useWireBaseExample4 wire .
wire: /home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample4/main.go:63:1: inject InitializeEvent: no provider found for awesomeProject/202006/useWireBaseExample4.People
        needed by awesomeProject/202006/useWireBaseExample4.Event in provider "NewEvent" (/home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample4/main.go:46:6)
wire: awesomeProject/202006/useWireBaseExample4: generate failed
wire: at least one generate failure
➜  useWireBaseExample4


错误提示中非常清楚的告诉我它找不到no provider found ,如果我们传给wire.Build 没有用的依赖,它依然会给我们提示告诉我们 unused provider "main.NewEventNumber"

➜  useWireBaseExample4 wire .
wire: /home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample4/main.go:67:1: inject InitializeEvent: unused provider "main.NewEventNumber"
wire: awesomeProject/202006/useWireBaseExample4: generate failed
wire: at least one generate failure

wire的高级用法

Binding Interfaces

依赖注入通常用于绑定接口的具体实现。通过下面的例子理解:

package main


import (
	"fmt"


	"github.com/google/wire"
)


type Fooer interface {
	Foo() string
}


type MyFooer string


func (b *MyFooer) Foo() string {
	return string(*b)
}


func provideMyFooer() *MyFooer {
	b := new(MyFooer)
	*b = "Hello, World!"
	return b
}


type Bar string


func provideBar(f Fooer) string {
	// f will be a *MyFooer.
	return f.Foo()
}




func InitializeEvent() string {
	wire.Build(provideMyFooer, provideBar, wire.Bind(new(Fooer), new(*MyFooer)))
	return ""
}
func main() {
	ret := InitializeEvent()
	fmt.Println(ret)
}


我们可以看到Fooer 是一个interface, MyFooer 实现了Fooer 这个接口,同时provideBar 的参数是Fooer 接口类型。代码中我们用了wire.Bind方法,为什么这么用呢?如果我们wire.Build的那段代码写成如下:

wire.Build(provideMyFooer, provideBar),再次用wire生成代码则会提示如下错误:

➜  useWireBaseExample5 wire .
wire: /home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample5/main.go:36:1: inject InitializeEvent: no provider found for awesomeProject/202006/useWireBaseExample5.Fooer
        needed by string in provider "provideBar" (/home/fan/codes/go_project/awesomeProject/202006/useWireBaseExample5/main.go:27:6)
wire: awesomeProject/202006/useWireBaseExample5: generate failed
wire: at least one generate failure

这是因为我们传递给provideBar 需要的是 Fooer 接口类型,我们传给wire.Build 的是provideMyFooer, provideBar 这个时候默认从依赖关系里,provideBar 没有找能够提供Fooer的provider, 虽然我们我们都知道MyFooer 实现了Fooer 这个接口。所以我们需要在wire.Build 里告诉它,我们传递provideMyFooer 就是provideBar的provider。wire.Bind 就是来做这件事情的。

wire.Bind 的第一个参数是接口类型的值的指针,第二个参数是实现第一个参数接口的类型的值的指针。

这样当我们在用wire生成代码的时候就正常了。

Struct Providers

wire还可以用于结构体的构造。先直接看使用的例子:

package main


import (
   "fmt"


   "github.com/google/wire"
)


type Foo int
type Bar int


func ProvideFoo() Foo {
   return Foo(1)
}


func ProvideBar() Bar {
   return Bar(2)
}


type FooBar struct {
   MyFoo Foo
   MyBar Bar
}


var Set = wire.NewSet(
   ProvideFoo,
   ProvideBar,
   wire.Struct(new(FooBar), "MyFoo", "MyBar"),
)


func injectFooBar() FooBar {
   wire.Build(Set)
   return FooBar{}
}


func main() {
   fooBar := injectFooBar()
   fmt.Println(fooBar)
}

上面的例子其实很简单,我们构造FooBar 结构体我们需要MyFoo 和 MyBar ,而ProvideFoo 和 ProvideBar 就是用于生成MyFoo 和 MyBar,wire.Struct 也可以帮我们做这件事情。我们通过wire生成的代码如下:

// Injectors from main.go:


func injectFooBar() FooBar {
   foo := ProvideFoo()
   bar := ProvideBar()
   fooBar := FooBar{
      MyFoo: foo,
      MyBar: bar,
   }
   return fooBar
}

wire.Struct 的第一个参数是所需结构类型的指针,后面的参数是要注入的字段的名称。可以使用一个特殊的字符串“ * ”作为告诉注入器注入所有字段的快捷方式。所以我们上面的代码也可以写成:wire.Struct(new(FooBar), "×") ,而当我们使用* 这种方式的时候可能会把一些不需要注入的字段注入了,如锁,那么类似这种情况,如果我们注入,通过wire:"-" 的方式告诉wire 该字段不进行注入。

type Foo struct {
    mu sync.Mutex `wire:"-"`
    Bar Bar
}

Binding Values

这个功能主要就是给数据类型绑定一个默认值,代码例子如下:

package main


import (
   "fmt"


   "github.com/google/wire"
)


type Foo struct {
   X int
}


func injectFoo() Foo {
   wire.Build(wire.Value(Foo{X: 11}))
   return Foo{}
}


func main() {
   foo := injectFoo()
   fmt.Println(foo)
}

我通过wire生成的代码如下:

// Code generated by Wire. DO NOT EDIT.


//go:generate wire
//+build !wireinject


package main


import (
   "fmt"
)


// Injectors from main.go:


func injectFoo() Foo {
   foo := _wireFooValue
   return foo
}


var (
   _wireFooValue = Foo{X: 11}
)


// main.go:


type Foo struct {
   X int
}


func main() {
   foo := injectFoo()
   fmt.Println(foo)
}

Use Fields of a Struct as Providers

有时,我们需要获取结构体的某些字段,按照我们已经使用的wire的用法,你可能会这样写代码:

package main


import (
   "fmt"


   "github.com/google/wire"
)


type Foo struct {
   S string
   N int
   F float64
}


func getS(foo Foo) string {
   return foo.S
}


func provideFoo() Foo {
   return Foo{S: "Hello, World!", N: 1, F: 3.14}
}


func injectedMessage() string {
   wire.Build(
      provideFoo,
      getS,
   )
   return ""
}


func main() {
   ret := injectedMessage()
   fmt.Println(ret)
}

这种用法当然也可以实现,但是wire其实提供了更好的办法来实现wire.FieldsOf, 我们将上面的代码进行更改如下,通过wire生成的代码其实和上面的是一样的:

package main


import (
   "fmt"


   "github.com/google/wire"
)


type Foo struct {
   S string
   N int
   F float64
}


func provideFoo() Foo {
   return Foo{S: "Hello, World!", N: 1, F: 3.14}
}


func injectedMessage() string {
   wire.Build(
      provideFoo,
      wire.FieldsOf(new(Foo), "S"),
   )
   return ""
}


func main() {
   ret := injectedMessage()
   fmt.Println(ret)
}

Cleanup functions

如果我们的Provider创建了一个需要做clean 的值,例如关闭文件,关闭数据连接..., 这里也是可以返回一个闭包来清理资源,注入器将使用它向调用者返回一个聚合的清理函数,或者如果稍后在注入器实现中调用的提供程序返回一个错误,则使用它来清理资源。

关于这个功能的使用,通过https://github.com/LyricTian/gin-admin 的代码中的使用,可以更加清楚。

作者在gin-admin/internal/app/app.go 中进行了初始化依赖注入器

// 初始化依赖注入器
injector, injectorCleanFunc, err := injector.BuildInjector()
if err != nil {
   return nil, err
}

我们看看wire生成的wire_gen.go代码:

// Injectors from wire.go:


func BuildInjector() (*Injector, func(), error) {
   auther, cleanup, err := InitAuth()
   if err != nil {
      return nil, nil, err
   }
   db, cleanup2, err := InitGormDB()
   if err != nil {
      cleanup()
      return nil, nil, err
   }
   role := &model.Role{
      DB: db,
   }
   roleMenu := &model.RoleMenu{
      DB: db,
   }
   menuActionResource := &model.MenuActionResource{
      DB: db,
   }
   user := &model.User{
      DB: db,
   }
   userRole := &model.UserRole{
      DB: db,
   }
   casbinAdapter := &adapter.CasbinAdapter{
      RoleModel:         role,
      RoleMenuModel:     roleMenu,
      MenuResourceModel: menuActionResource,
      UserModel:         user,
      UserRoleModel:     userRole,
   }
   syncedEnforcer, cleanup3, err := InitCasbin(casbinAdapter)
   if err != nil {
      cleanup2()
      cleanup()
      return nil, nil, err
   }
   demo := &model.Demo{
      DB: db,
   }
   bllDemo := &bll.Demo{
      DemoModel: demo,
   }
   apiDemo := &api.Demo{
      DemoBll: bllDemo,
   }
   menu := &model.Menu{
      DB: db,
   }
   menuAction := &model.MenuAction{
      DB: db,
   }
   login := &bll.Login{
      Auth:            auther,
      UserModel:       user,
      UserRoleModel:   userRole,
      RoleModel:       role,
      RoleMenuModel:   roleMenu,
      MenuModel:       menu,
      MenuActionModel: menuAction,
   }
   apiLogin := &api.Login{
      LoginBll: login,
   }
   trans := &model.Trans{
      DB: db,
   }
   bllMenu := &bll.Menu{
      TransModel:              trans,
      MenuModel:               menu,
      MenuActionModel:         menuAction,
      MenuActionResourceModel: menuActionResource,
   }
   apiMenu := &api.Menu{
      MenuBll: bllMenu,
   }
   bllRole := &bll.Role{
      Enforcer:      syncedEnforcer,
      TransModel:    trans,
      RoleModel:     role,
      RoleMenuModel: roleMenu,
      UserModel:     user,
   }
   apiRole := &api.Role{
      RoleBll: bllRole,
   }
   bllUser := &bll.User{
      Enforcer:      syncedEnforcer,
      TransModel:    trans,
      UserModel:     user,
      UserRoleModel: userRole,
      RoleModel:     role,
   }
   apiUser := &api.User{
      UserBll: bllUser,
   }
   routerRouter := &router.Router{
      Auth:           auther,
      CasbinEnforcer: syncedEnforcer,
      DemoAPI:        apiDemo,
      LoginAPI:       apiLogin,
      MenuAPI:        apiMenu,
      RoleAPI:        apiRole,
      UserAPI:        apiUser,
   }
   engine := InitGinEngine(routerRouter)
   injector := &Injector{
      Engine:         engine,
      Auth:           auther,
      CasbinEnforcer: syncedEnforcer,
      MenuBll:        bllMenu,
   }
   return injector, func() {
      cleanup3()
      cleanup2()
      cleanup()
   }, nil
}

而当程序退出的时候这上面代码返回的那些清理操作都会被执行:

// Run 运行服务
func Run(ctx context.Context, opts ...Option) error {
   var state int32 = 1
   sc := make(chan os.Signal, 1)
   signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
   cleanFunc, err := Init(ctx, opts...)
   if err != nil {
      return err
   }


EXIT:
   for {
      sig := <-sc
      logger.Printf(ctx, "接收到信号[%s]", sig.String())
      switch sig {
      case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
         atomic.CompareAndSwapInt32(&state, 1, 0)
         break EXIT
      case syscall.SIGHUP:
      default:
         break EXIT
      }
   }
   // 在这里执行了清理工作
   cleanFunc()
   logger.Printf(ctx, "服务退出")
   time.Sleep(time.Second)
   os.Exit(int(atomic.LoadInt32(&state)))
   return nil
}

延伸阅读

  • https://github.com/google/wire

golang框架之go-zero基础框架搭建 作为golang服务的诸多框架,下面为大家介绍一下go-zero这款框架从搭建到使用中所遇到的诸多问题。 本章节从0开始,通过脚本、API的方式,帮助大家入门级搭建gozero基础框架,实现缓存的使用和数据库的基础增删改查。仅需创建几个文件,即可完成服务搭建。 阅读详情

相关推荐

golang学习笔记30——golang 中代码仓库的 h1 和 go.mod h1 不一致的修正方法

在 Golang 中,go.mod 文件记录了项目的依赖模块及其版本信息。每个依赖模块都有一个唯一的哈希值(h1),用于确保模块的完整性和一致性。在 Golang 项目开发中,遇到代码仓库的 h1 和 go.mod 文件中的 h1 不一致的情况是比较常见的。可以通过使用 go mod tidy 命令或者手动更新 h1 值的方法来修正这个问题。在实际项目中,建议定期运行 go mod tidy 命令,以确保项目的依赖关系正确且一致。希望本文对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。

GoppViper的博客 1254

在使用 provide时, 出现injection“ xxxx“ not found (关于学VUE3用provide遇到的坑)

我第一次接触到 setup语法的 provide/inject,没有认真阅读文档,帮助其他人完成毕业设计。使用el-dialog来写登录页面,我希望当我登陆成功时,我会在上面的导航条上刷新一个用户的头像。这样就可以轻松地重新登录指定的部件。我只是愚蠢的使用了 provide但我并不知道 provide是用来传递父元件的属性方法的,我没有料到我的el-dialog会被写入到最上面的导航条中。这样就出现了这样一种有趣的情况,即通过provide()方式将该方法传递到父元件。

qq_34920595的博客 3095

【Golang学习之旅】Go 语言基础语法概览

✅ 适合人群: Golang 新手 | 后端开发者 | 面试求职者✅ 文章亮点: 语法详解 + 实战代码 + 高质量示例✅ 目标: 10 分钟快速掌握 Golang 基础语法,助你高效写项目!Go 语言(Golang)是Google开发的一门静态强类型并发支持强语法简洁的编程语言,广泛应用于后端开发云计算微服务区块链等领域。🔹 Go 语言的 4 大核心特点:✅ 语法简洁(减少代码冗余,开发高效)✅ 高性能(比 Python 快,比 Java 简单)

程序员林北北的博客 1408

Golang Python通过gRPC跨语言调用DEMO

1、首先Python端和Golang端都要安装gRPC包和相应的源码生成工具 python(适用于3.5以上版本): python -m pip install grpcio python -m pip install grpcio-tools golang: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@

ChenZihao 949

Go语言算法实战教程:深入学习Golang算法

通过理解树的遍历和重构方法以及特殊形态的二叉树,我们能够在面对各种树相关问题时,选择恰当的数据结构和算法,以达到最优的性能表现。

weixin_42608299的博客 1233

FRM(Financial Risk Manager,金融风险管理师)知识点总结归纳

FRM知识点总结(150字) FRM考试分为两级:一级侧重基础理论和工具,包含风险管理基础、定量分析、金融市场产品、估值与风险建模四大模块,涵盖风险管理框架、统计学方法、衍生品定价及VaR计算等核心内容。二级聚焦实际应用,分为市场风险、信用风险、操作风险、流动性风险、投资管理和市场前沿六大模块,深入探讨高级风险度量技术(如极值理论)、信用风险模型、巴塞尔协议及新兴风险议题。考试体系贯穿"风险识别→度量→管理→监管"逻辑主线,强调定量分析与实务案例的结合,覆盖传统金融风险和区块链、ESG等

极客神殿 1441

golang依赖注入工具wire指南

wire与依赖注入 Wire 是一个的Golang依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入,Java体系中最出名的Spring框架采用运行时注入,个人认为这是wire和其他依赖注入最大的不同之处。 依赖注入(Dependency Injection)也称作控制反转(Inversion of Control),个人给控制反转下的定义如下: 当前对象需要的依赖对象由外部提供(通常是IoC容器),外部负责依赖对象的构造等操作,当前对象只负责调用,而不关心依赖对象的构造。即依赖对象的控制权交给了Io

m0_55220870的博客 2129

超详细Tokenizer——文本训练数据预处理

Tokenizer是Keras库中的一个文本处理工具,它用于将文本分词并构建词汇表,同时将文本映射到整数序列。这对于自然语言处理任务非常有用,例如文本分类、情感分析、机器翻译等。

halo的博客 3701

依赖注入框架 wire

在上一篇文章当中我们讲到了项目的目录结构,大体上水平切分为了四层,然后再根据需要进行垂直切分,然后由于我们大量的使用到了接口和依赖注入的手段,所以在项目初始化的时候如果手动进行依赖关系的初始化会比较麻烦,这时候就需要用到依赖注入的框架了。 在刚开始接触 go 的那一段时间,我是比较排斥使用太多框架的,觉得保持简单更加重要,这种想法在很长一段时间(大概两年左右)都没有问题,直到正式工作一段时间之后发现,随着开发合作的同学的增多以及部门的要求增加,项目启动时的依赖越来越多,依赖之间还有先后顺序,有一些甚至是隐式

笔记 1570

第五章 PCIe介绍 5.1-5.7

为什么SSD要用PCIe接口?因为它快,比SATA快。通道,PCIe最多可以有32个通道。两个设备之间的PCIe连接,叫做一个Link。如下图,设备A和设备B是个双向连接,两个PCIe设备之间,有专门的发送和接收通道,数据可以同时往两个方向传输,PCIe Spec称这种工作模式为,可以理解为。和PCIe一样,SATA有独立的发送和接收通道。但与PCIe工作模式不一样,同一时间,SATA只有一条通道可以进行数据传输。这种工作模式称为。与SATA单通道不同,PCIe连接可以通过增加通道数扩展带宽。

fengbaobaola的博客 2969

SWIFT报文实战指南:从MT700到MT799的信用证操作全解析

本文深入解析了国际贸易中信用证操作的核心SWIFT报文流程,从开证的MT700到各类通知、修改及结算报文(如MT707、MT730、MT754、MT732/734等)。通过实战案例与关键字段拆解,指导外贸与银行从业者精准处理报文,规避风险,并探讨了利用自动化工具提升处理效率的方法,助力保障跨境交易顺畅。

weixin_29015801的博客 878

报错NullInjectorError: No provider for InjectionToken 以及 No provider for Service解决

NullInjectorError: No provider for InjectionToken 这个是 1因为app.module.ts 没有import这个报错的module import {} from '' imports:[] 2或者 app.module.ts 里面 providers:[ ] 没有这个service

weixin_41147129的博客 5688

STM32N6570-DK识别STLINK问题,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 2209

[GO语言基础] 一.为什么我要学习Golang以及GO语言入门普及

作为网络安全初学者,会遇到采用Go语言开发的恶意样本。因此从今天开始从零讲解Golang编程语言,一方面是督促自己不断前行且学习新知识;另一方面是分享与读者,希望大家一起进步。这系列文章入门部分将参考“尚硅谷”韩顺平老师的视频和书籍《GO高级编程》,详见参考文献,并结合作者多年的编程经验进行学习和丰富,且看且珍惜吧!后续会结合网络安全进行GO语言实战深入,驾驭~ 这些年我学过各种编程语言,从最早的C语言到C++,再到C#、PHP、JAVA,再到IOS开发、Python,到最新的GO语言,学得是真的杂。有时

杨秀璋的专栏 12万+

在 ABAP Cloud 里优雅读取与解析 XML:用 sXML Reader 把接口数据落成结构化 ABAP

ABAP Cloud中XML解析实践 摘要:本文介绍了在ABAP Cloud环境下解析XML数据的最佳实践。针对企业系统中常见的XML格式数据,文章推荐使用sXML库中的CL_SXML_STRING_READER进行流式解析,而非传统的DOM方式。重点解析了命名空间(Namespace)、属性(Attribute)和CDATA等关键概念的处理方法,并比较了两种读取属性的策略:通扫所有属性(NEXT_ATTRIBUTE)和精准取值(GET_ATTRIBUTE_VALUE)。文章提供了完整的ABAP Cloud

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 190

基于Stateflow的驱动电机控制系统模式管理与安全策略研究

摘要 本文基于Stateflow状态机建模工具,研究了驱动电机控制系统的模式管理架构与安全策略实现方法。通过分析电机控制系统的工作模式,设计了包含初始化、待机、校准、对齐、运行、故障、复位七种状态的有限状态机模型,并建立了三级故障处理机制和降额保护策略。基于Simulink/Stateflow搭建的仿真模型实现了模式切换逻辑和安全管理功能,验证了系统能够正确响应各类控制事件,故障处理机制可有效保护系统安全。研究成果为电动汽车电机控制系统的工程开发提供了参考。 关键词:Stateflow;驱动电机控制;模式管

AI的内部世界的博客 58

【Go语言学习系列02】Go开发工具链介绍

全面解析Go语言工具链,掌握go build、go fmt、go test等必备命令,快速提升Go开发效率与代码质量

GopherTribe的博客 1594

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Go-Golang学习总结笔记

资源 某hub会审核不通过,自己去找吧 书籍 网站 基础 go官方中文网 菜鸟教程 C语言网 go语言中文网 golang系列 视频 基础 安装 依赖管理 数据类型 变量和常量 流程控制 函数 包 面向对象编程 提高 ...

关注网络安全、云原生安全 2万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Golang 生态学习

虽然 Go 在人工智能领域不如 Python 那样流行,但由于其出色的性能和并发支持,Go 也开始被用于一些 AI 和机器学习(ML)项目,尤其是对于需要高并发、高性能的任务,Go 可以提供优势。• 《Go语言圣经》(The Go Programming Language):这是学习 Go 语言的经典书籍,涵盖了 Go 的所有基础内容。它被广泛应用于供应链、金融等行业的区块链解决方案。:Go 的并发模型(goroutines 和 channels)是 Go 的强项,学习并发编程是使用 Go 的重要步骤。

A_Tevens的博客 1515

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Golang 必知必会Go Mod命令

通过以上可以轻松了解在Golang中go mod的使用方式。Go mod提供了一系列操作modules的命令,所有的go命令中现在已经内置了对module的支持,而不仅仅是go mod命令。例如使用go get时,会经常自动在后台添加、移除、升级、降级依赖包版本。

猫轻王的博客 2万+

从Java到Go:Java工程师的Golang学习指南

作为一名Java工程师,你可能已经习惯了Java的面向对象编程、强大的生态系统以及丰富的工具链。然而,随着Go语言(Golang)在云计算、微服务和并发编程领域的崛起,学习Go语言成为了一个非常有价值的选择。本文将通过对比Java和Go语言的方式,帮助你快速掌握Go语言的核心概念,并通过详细的案例讲解,帮助你理解两者之间的异同。 Go 对比: Go 对比: Go 对比: Go 对比:Java通过类和接口实现多线程编程,或者使用包中的高级并发工具。 Go Go语言通过实现并发,是轻量级的线程,由Go运

SunnyRivers 1751

为什么劝你要学习Golang以及GO语言(Go语言知识普及)

为什么劝你要学习Golang以及GO语言

m0_69234258的博客 4973

go系列&运维系列&Windows系列:golang学习7:编译为可被C语言调用的库

golang学习7:编译为可被C语言调用的库

weixin_54626591的博客 1228

【Go语言学习系列00】Go语言学习系列导航

本文是Go语言学习系列的导航文档,提供了从入门到精通的学习路径,包含各阶段文章链接和内容概述,帮助读者系统化学习Go语言。

GopherTribe的博客 1575

Golang学习总结

一、基本语法 Golang 设计理念:一种事情有且只有一种方法完成 软件包安装 直接官网下载好,配置安装下环境变量即可 /etc/profile # 在这个文件写入会对所有用户生效 ~/.profile # 在这个文件写入只会对当前用户生效 # 以上两个文件中任意选择一个 # 将 Go 的安装目录记录下,比如安装目录为 /home/CHAO/Document/go/bin export GOROOT=/usr/local/go #这个表示go

weixin_45541397的博客 2271
上一篇: Centos下安装Google Chrome做网页性能分析
下一篇: Nmap常见扫描方式流量分析
后场技术
博客等级 码龄11年 156粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值