补充ioc

This commit is contained in:
yumaojun03 2024-12-15 11:36:22 +08:00
parent 5b25cfefce
commit c3a407ba0b
15 changed files with 184 additions and 24 deletions

View File

@ -318,4 +318,53 @@ curl --location 'http://127.0.0.1:8080/vblog/api/v1/tokens' \
### 问题 ### 问题
+ 手动管理: main, 自己组装对象, 业务越复杂,组装难度越高
![](./docs/oop.png)
+ ioc: 引入了一个中间层, 这个中间层负责对象的管理, 自己对象自己去ioc获取依赖而不是我们开发者 把依赖传递给他,完成 对象的 依赖 由被动 变成主动, ioc, 依赖倒置
![](./docs/ioc.png)
### 基于mcube ioc来改造
![alt text](image.png)
https://www.mcube.top/docs/framework/
1. 对象注册到ioc: 把我们的对象实现了1个IOC对象(符合Ioc接口定义的对象), 可以通过继承基础类直接实现接口ObjectImpl
+ 2 APIHandler: TokenApiHandler, BlogApiHandler
+ 3 Controller: UserServiceImpl, TokenServiceImpl, BlogServiceImpl
对象注册
```go
func init() {
ioc.Controller().Registry(&TokenServiceImpl{})
}
// 定义一个struct, 用于实现 UserService就是刚才定义的接口
// 怎么才能判断这个结构体没有实现这个接口
type TokenServiceImpl struct {
ioc.ObjectImpl
// user service
user user.AdminService
}
func (*TokenServiceImpl) Name() string {
return token.AppName
}
// 他需要自己去获取依赖通过ioc
func (i *TokenServiceImpl) Init() error {
i.user = user.GetService()
return nil
}
```
对象获取
```go
func GetService() Service {
return ioc.Controller().Get(AppName).(Service)
}
```

7
vblog/apps/registry.go Normal file
View File

@ -0,0 +1,7 @@
package apps
import (
_ "gitlab.com/go-course-project/go17/vblog/apps/blog/impl"
_ "gitlab.com/go-course-project/go17/vblog/apps/token/impl"
_ "gitlab.com/go-course-project/go17/vblog/apps/user/impl"
)

View File

@ -5,29 +5,35 @@ import (
"fmt" "fmt"
"github.com/infraboard/mcube/v2/exception" "github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/datasource" "github.com/infraboard/mcube/v2/ioc/config/datasource"
"gitlab.com/go-course-project/go17/vblog/apps/token" "gitlab.com/go-course-project/go17/vblog/apps/token"
"gitlab.com/go-course-project/go17/vblog/apps/user" "gitlab.com/go-course-project/go17/vblog/apps/user"
"gitlab.com/go-course-project/go17/vblog/apps/user/impl"
) )
var TokenService token.Service = &TokenServiceImpl{ func init() {
user: impl.UserService, ioc.Controller().Registry(&TokenServiceImpl{})
}
func NewTokenService(user user.AdminService) token.Service {
return &TokenServiceImpl{
user: impl.UserService,
}
} }
// 定义一个struct, 用于实现 UserService就是刚才定义的接口 // 定义一个struct, 用于实现 UserService就是刚才定义的接口
// 怎么才能判断这个结构体没有实现这个接口 // 怎么才能判断这个结构体没有实现这个接口
type TokenServiceImpl struct { type TokenServiceImpl struct {
ioc.ObjectImpl
// user service // user service
user user.AdminService user user.AdminService
} }
func (*TokenServiceImpl) Name() string {
return token.AppName
}
// 他需要自己去获取依赖通过ioc
func (i *TokenServiceImpl) Init() error {
i.user = user.GetService()
return nil
}
// IssueToken implements token.Service. // IssueToken implements token.Service.
func (t *TokenServiceImpl) IssueToken(ctx context.Context, in *token.IssueTokenRequest) (*token.Token, error) { func (t *TokenServiceImpl) IssueToken(ctx context.Context, in *token.IssueTokenRequest) (*token.Token, error) {
if err := in.Validate(); err != nil { if err := in.Validate(); err != nil {

View File

@ -5,7 +5,6 @@ import (
"testing" "testing"
"gitlab.com/go-course-project/go17/vblog/apps/token" "gitlab.com/go-course-project/go17/vblog/apps/token"
"gitlab.com/go-course-project/go17/vblog/apps/token/impl"
) )
var ( var (
@ -20,7 +19,7 @@ var (
// https://gitee.com/infraboard/go-course/blob/master/day09/go-hash.md#bcrypt // https://gitee.com/infraboard/go-course/blob/master/day09/go-hash.md#bcrypt
func TestIssueToken(t *testing.T) { func TestIssueToken(t *testing.T) {
req := token.NewIssueTokenRequest("admin", "123456") req := token.NewIssueTokenRequest("admin", "123456")
ins, err := impl.TokenService.IssueToken(ctx, req) ins, err := token.GetService().IssueToken(ctx, req)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -29,7 +28,7 @@ func TestIssueToken(t *testing.T) {
func TestValidateToken(t *testing.T) { func TestValidateToken(t *testing.T) {
req := token.NewValidateTokenRequest("51bf49f5-12a2-406a-baf8-3f99d985b41a") req := token.NewValidateTokenRequest("51bf49f5-12a2-406a-baf8-3f99d985b41a")
ins, err := impl.TokenService.ValidateToken(ctx, req) ins, err := token.GetService().ValidateToken(ctx, req)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -3,9 +3,18 @@ package token
import ( import (
"context" "context"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/validator" "github.com/infraboard/mcube/v2/ioc/config/validator"
) )
const (
AppName = "token"
)
func GetService() Service {
return ioc.Controller().Get(AppName).(Service)
}
// 业务域 // 业务域
type Service interface { type Service interface {
UserService UserService

View File

@ -3,16 +3,28 @@ package impl
import ( import (
"context" "context"
"github.com/infraboard/mcube/v2/ioc"
"github.com/infraboard/mcube/v2/ioc/config/datasource" "github.com/infraboard/mcube/v2/ioc/config/datasource"
"gitlab.com/go-course-project/go17/vblog/apps/user" "gitlab.com/go-course-project/go17/vblog/apps/user"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
var UserService user.Service = &UserServiceImpl{} func init() {
ioc.Controller().Registry(&UserServiceImpl{})
}
// int(10) -> (int)(10)
var _ user.Service = (*UserServiceImpl)(nil)
// 定义一个struct, 用于实现 UserService就是刚才定义的接口 // 定义一个struct, 用于实现 UserService就是刚才定义的接口
// 怎么才能判断这个结构体没有实现这个接口 // 怎么才能判断这个结构体没有实现这个接口
type UserServiceImpl struct { type UserServiceImpl struct {
// 这个对象 就实现ioc Object接口, 他就能被放到ioc
ioc.ObjectImpl
}
func (i *UserServiceImpl) Name() string {
return user.AppName
} }
// DescribeUser implements user.Service. // DescribeUser implements user.Service.

View File

@ -5,7 +5,6 @@ import (
"testing" "testing"
"gitlab.com/go-course-project/go17/vblog/apps/user" "gitlab.com/go-course-project/go17/vblog/apps/user"
"gitlab.com/go-course-project/go17/vblog/apps/user/impl"
) )
var ( var (
@ -22,7 +21,7 @@ func TestRegistry(t *testing.T) {
req := user.NewRegistryRequest() req := user.NewRegistryRequest()
req.Username = "test02" req.Username = "test02"
req.Password = "123456" req.Password = "123456"
ins, err := impl.UserService.Registry(ctx, req) ins, err := user.GetService().Registry(ctx, req)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -30,7 +29,7 @@ func TestRegistry(t *testing.T) {
} }
func TestDescribeUser(t *testing.T) { func TestDescribeUser(t *testing.T) {
ins, err := impl.UserService.DescribeUser(ctx, &user.DescribeUserRequest{ ins, err := user.GetService().DescribeUser(ctx, &user.DescribeUserRequest{
user.DESCRIBE_BY_USERNAME, "admin", user.DESCRIBE_BY_USERNAME, "admin",
}) })
if err != nil { if err != nil {
@ -38,5 +37,5 @@ func TestDescribeUser(t *testing.T) {
} }
// //
// if ins.Password = in.Password // if ins.Password = in.Password
t.Log(ins.CheckPassword("1234567")) t.Log(ins)
} }

View File

@ -1,6 +1,19 @@
package user package user
import "context" import (
"context"
"github.com/infraboard/mcube/v2/ioc"
)
// 获取实现从ioc
func GetService() Service {
return ioc.Controller().Get(AppName).(Service)
}
const (
AppName = "user"
)
type Service interface { type Service interface {
AdminService AdminService

View File

@ -4,6 +4,9 @@ import (
"os" "os"
"github.com/infraboard/mcube/v2/ioc" "github.com/infraboard/mcube/v2/ioc"
// 导入程序所有的对
_ "gitlab.com/go-course-project/go17/vblog/apps"
) )
func LoadConfig() { func LoadConfig() {

66
vblog/docs/ioc.drawio Normal file
View File

@ -0,0 +1,66 @@
<mxfile host="65bd71144e">
<diagram id="QJ9SYQZkHZ_Pkl1wdixx" name="第 1 页">
<mxGraphModel dx="947" dy="479" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="19" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="18">
<mxGeometry relative="1" as="geometry">
<mxPoint x="423" y="280" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="20" value="对象注册" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="19">
<mxGeometry x="0.1852" y="-4" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="30" y="350" width="785" height="130" as="geometry"/>
</mxCell>
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="25" y="100" width="785" height="180" as="geometry"/>
</mxCell>
<mxCell id="23" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.309;entryY=0.991;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="24" value="get token service Impl" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="23">
<mxGeometry x="0.1968" y="-2" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="4" value="&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-decoration-line: underline;&quot;&gt;TokenApiHandler&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="207.5" y="380" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="8" value="&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-decoration-line: underline;&quot;&gt;BlogApiHandler&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="517.5" y="380" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="25" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.551;entryY=0.997;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="10" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="26" value="get user service impl" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="25">
<mxGeometry x="0.225" y="-1" relative="1" as="geometry">
<mxPoint x="51" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="10" value="&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-decoration-line: underline;&quot;&gt;TokenServiceImpl&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="367.5" y="380" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="11" value="&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;div style=&quot;line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-decoration-line: underline;&quot;&gt;BlogServiceImpl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="667.5" y="380" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="12" value="&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-align: left; text-decoration-line: underline;&quot;&gt;UserServiceImpl&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;div style=&quot;color: rgb(248, 248, 242); background-color: rgb(39, 40, 34); font-family: &amp;quot;Cascadia Code NF&amp;quot;, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace, Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: rgb(166, 226, 46); text-align: left; text-decoration-line: underline;&quot;&gt;name: user&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="57.5" y="380" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="13" value="ioc:&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: rgb(51, 51, 51); font-family: Arial, sans-serif; font-size: 13px; text-align: start; background-color: rgb(255, 255, 255);&quot;&gt;Inversion of Control缩写为&lt;/span&gt;&lt;span style=&quot;color: rgb(247, 49, 49); font-family: Arial, sans-serif; font-size: 13px; text-align: start; background-color: rgb(255, 255, 255);&quot;&gt;IoC&lt;/span&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="311.25" y="60" width="232.5" height="30" as="geometry"/>
</mxCell>
<mxCell id="27" value="Text" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="400" y="400" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="29" value="&lt;h1&gt;对象生命周期&lt;/h1&gt;&lt;p&gt;1. 对象注册&lt;/p&gt;&lt;p&gt;2. 对象配置(通过配置文件读取)&lt;/p&gt;&lt;p&gt;3. 初始化(基础属性, 依然依赖)&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
<mxGeometry x="30" y="500" width="200" height="230" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

BIN
vblog/docs/ioc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
vblog/docs/oop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
vblog/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -5,9 +5,8 @@ import (
blogApi "gitlab.com/go-course-project/go17/vblog/apps/blog/api" blogApi "gitlab.com/go-course-project/go17/vblog/apps/blog/api"
blogImpl "gitlab.com/go-course-project/go17/vblog/apps/blog/impl" blogImpl "gitlab.com/go-course-project/go17/vblog/apps/blog/impl"
"gitlab.com/go-course-project/go17/vblog/apps/token"
tokenApi "gitlab.com/go-course-project/go17/vblog/apps/token/api" tokenApi "gitlab.com/go-course-project/go17/vblog/apps/token/api"
tokenImpl "gitlab.com/go-course-project/go17/vblog/apps/token/impl"
userImpl "gitlab.com/go-course-project/go17/vblog/apps/user/impl"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/infraboard/mcube/v2/ioc/config/http" "github.com/infraboard/mcube/v2/ioc/config/http"
@ -21,7 +20,7 @@ func main() {
server := gin.Default() server := gin.Default()
// 注册业务模块的路有 // 注册业务模块的路有
tokenApi.NewTokenApiHandler(tokenImpl.NewTokenService(&userImpl.UserServiceImpl{})).Registry(server) tokenApi.NewTokenApiHandler(token.GetService()).Registry(server)
blogApi.NewBlogApiHandler(&blogImpl.BlogServiceImpl{}).Registry(server) blogApi.NewBlogApiHandler(&blogImpl.BlogServiceImpl{}).Registry(server)
// ... 50 个API // ... 50 个API
// //

View File

@ -9,8 +9,6 @@ import (
"github.com/infraboard/mcube/v2/exception" "github.com/infraboard/mcube/v2/exception"
"github.com/infraboard/mcube/v2/http/gin/response" "github.com/infraboard/mcube/v2/http/gin/response"
"gitlab.com/go-course-project/go17/vblog/apps/token" "gitlab.com/go-course-project/go17/vblog/apps/token"
"gitlab.com/go-course-project/go17/vblog/apps/token/impl"
userImpl "gitlab.com/go-course-project/go17/vblog/apps/user/impl"
) )
// // HandlerFunc defines the handler used by gin middleware as return value. // // HandlerFunc defines the handler used by gin middleware as return value.
@ -30,7 +28,7 @@ func Auth(c *gin.Context) {
accessToken = tkList[1] accessToken = tkList[1]
} }
// 2. 校验Token // 2. 校验Token
tk, err := impl.NewTokenService(&userImpl.UserServiceImpl{}).ValidateToken(c.Request.Context(), token.NewValidateTokenRequest(accessToken)) tk, err := token.GetService().ValidateToken(c.Request.Context(), token.NewValidateTokenRequest(accessToken))
if err != nil { if err != nil {
response.Failed(c, exception.NewUnauthorized("令牌校验失败: %s", err)) response.Failed(c, exception.NewUnauthorized("令牌校验失败: %s", err))
c.Abort() c.Abort()