精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Go 語言官方依賴注入工具 Wire 使用指北

精選
開發 開發工具
通過調研,了解到 Golang 中常用的依賴注入工具主要有 Inject 、Dig 等。但是今天主要介紹的是 Go 團隊開發的 Wire,一個編譯期實現依賴注入的工具。

1. 前言

接觸 Golang 有一段時間了,發現 Golang 同樣需要類似 Java 中 Spring 一樣的依賴注入框架。如果項目規模比較小,是否有依賴注入框架問題不大,但當項目變大之后,有一個合適的依賴注入框架是十分必要的。通過調研,了解到 Golang 中常用的依賴注入工具主要有 Inject 、Dig 等。但是今天主要介紹的是 Go 團隊開發的 Wire,一個編譯期實現依賴注入的工具。

2. 依賴注入(DI)是什么

說起依賴注入?就要引出另一個名詞控制反轉?( IoC )。IoC 是一種設計思想,其核心作用是降低代碼的耦合度。依賴注入?是一種實現控制反轉且用于解決依賴性問題的設計模式。

舉個例子,假設我們代碼分層關系是 dal 層連接數據庫,負責數據庫的讀寫操作。那么我們的 dal 層的上一層 service 負責調用 dal 層處理數據,在我們目前的代碼中,它可能是這樣的:

// dal/user.go

func (u *UserDal) Create(ctx context.Context, data *UserCreateParams) error {
db := mysql.GetDB().Model(&entity.User{})
user := entity.User{
Username: data.Username,
Password: data.Password,
}

return db.Create(&user).Error
}

// service/user.go
func (u *UserService) Register(ctx context.Context, data *schema.RegisterReq) (*schema.RegisterRes, error) {
params := dal.UserCreateParams{
Username: data.Username,
Password: data.Password,
}

err := dal.GetUserDal().Create(ctx, params)
if err != nil {
return nil, err
}

registerRes := schema.RegisterRes{
Msg: "register success",
}

return &registerRes, nil
}

在這段代碼里,層級依賴關系為 service -> dal -> db,上游層級通過 Getxxx?實例化依賴。但在實際生產中,我們的依賴鏈比較少是垂直依賴關系,更多的是橫向依賴。即我們一個方法中,可能要多次調用Getxxx的方法,這樣使得我們代碼極不簡潔。

不僅如此,我們的依賴都是寫死的,即依賴者的代碼中寫死了被依賴者的生成關系。當被依賴者的生成方式改變,我們也需要改變依賴者的函數,這極大的增加了修改代碼量以及出錯風險。

接下來我們用依賴注入的方式對代碼進行改造:

// dal/user.go
type UserDal struct{
DB *gorm.DB
}

func NewUserDal(db *gorm.DB) *UserDal{
return &UserDal{
DB: db
}
}

func (u *UserDal) Create(ctx context.Context, data *UserCreateParams) error {
db := u.DB.Model(&entity.User{})
user := entity.User{
Username: data.Username,
Password: data.Password,
}

return db.Create(&user).Error
}

// service/user.go
type UserService struct{
UserDal *dal.UserDal
}

func NewUserService(userDal dal.UserDal) *UserService{
return &UserService{
UserDal: userDal
}
}

func (u *UserService) Register(ctx context.Context, data *schema.RegisterReq) (*schema.RegisterRes, error) {
params := dal.UserCreateParams{
Username: data.Username,
Password: data.Password,
}

err := u.UserDal.Create(ctx, params)
if err != nil {
return nil, err
}

registerRes := schema.RegisterRes{
Msg: "register success",
}

return &registerRes, nil
}

// main.go
db := mysql.GetDB()
userDal := dal.NewUserDal(db)
userService := dal.NewUserService(userDal)

如上編碼情況中,我們通過將 db 實例對象注入到 dal 中,再將 dal 實例對象注入到 service 中,實現了層級間的依賴注入。解耦了部分依賴關系。

在系統簡單、代碼量少的情況下上面的實現方式確實沒什么問題。但是項目龐大到一定程度,結構之間的關系變得非常復雜時,手動創建每個依賴,然后層層組裝起來的方式就會變得異常繁瑣,并且容易出錯。這個時候勇士 wire 出現了!

3. Wire Come

3.1 簡介

Wire 是一個輕巧的 Golang 依賴注入工具。它由 Go Cloud 團隊開發,通過自動生成代碼的方式在編譯期完成依賴注入。它不需要反射機制,后面會看到, Wire 生成的代碼與手寫無異。

3.2 快速使用

wire 的安裝:

go get github.com/google/wire/cmd/wire

上面的命令會在 $GOPATH/bin? 中生成一個可執行程序 wire?,這就是代碼生成器。可以把$GOPATH/bin? 加入系統環境變量 $PATH? 中,所以可直接在命令行中執行 wire 命令。

下面我們在一個例子中看看如何使用 wire。

現在我們有這樣的三個類型:

type Message string
type Channel struct {
Message Message
}
type BroadCast struct {
Channel Channel
}

三者的 init 方法:

func NewMessage() Message {
return Message("Hello Wire!")
}
func NewChannel(m Message) Channel {
return Channel{Message: m}
}
func NewBroadCast(c Channel) BroadCast {
return BroadCast{Channel: c}
}

假設 Channel 有一個 GetMsg 方法,BroadCast 有一個 Start 方法:

func (c Channel) GetMsg() Message {
return c.Message
}

func (b BroadCast) Start() {
msg := b.Channel.GetMsg()
fmt.Println(msg)
}

如果手動寫代碼的話,我們的寫法應該是:

func main() {
message := NewMessage()
channel := NewChannel(message)
broadCast := NewBroadCast(channel)

broadCast.Start()
}

如果使用 wire,我們需要做的就變成如下的工作了:

  1. 提取一個 init 方法 InitializeBroadCast:
func main() {
b := demo.InitializeBroadCast()

b.Start()
}
  1. 編寫一個 wire.go 文件,用于 wire 工具來解析依賴,生成代碼:
//+build wireinject

package demo

func InitializeBroadCast() BroadCast {
wire.Build(NewBroadCast, NewChannel, NewMessage)
return BroadCast{}
}

注意:需要在文件頭部增加構建約束://+build wireinject

  1. 使用 wire 工具,生成代碼,在 wire.go 所在目錄下執行命令:wire gen wire.go。會生成如下代碼,即在編譯代碼時真正使用的Init函數:
// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject
func InitializeBroadCast() BroadCast {
message := NewMessage()
channel := NewChannel(message)
broadCast := NewBroadCast(channel)
return broadCast
}

我們告訴 wire?,我們所用到的各種組件的 init? 方法(NewBroadCast?, NewChannel?, NewMessage?),那么 wire 工具會根據這些方法的函數簽名(參數類型/返回值類型/函數名)自動推導依賴關系。

wire.go? 和 wire_gen.go? 文件頭部位置都有一個 +build?,不過一個后面是 wireinject?,另一個是 !wireinject。+build? 其實是 Go 語言的一個特性。類似 C/C++ 的條件編譯,在執行 go build? 時可傳入一些選項,根據這個選項決定某些文件是否編譯。wire? 工具只會處理有wireinject? 的文件,所以我們的 wire.go? 文件要加上這個。生成的 wire_gen.go? 是給我們來使用的,wire? 不需要處理,故有 !wireinject。

3.3 基礎概念

Wire? 有兩個基礎概念,Provider?(構造器)和 Injector(注入器)

  • Provider? 實際上就是生成組件的普通方法,這些方法接收所需依賴作為參數,創建組件并將其返回。我們上面例子的 NewBroadCast 就是 Provider。
  • Injector? 可以理解為 Providers 的連接器,它用來按依賴順序調用 Providers 并最終返回構建目標。我們上面例子的 InitializeBroadCast 就是 Injector。

4. Wire使用實踐

下面簡單介紹一下 wire 在飛書問卷表單服務中的應用。

飛書問卷表單服務的 project? 模塊中將 handler 層、service 層和 dal 層的初始化通過參數注入的方式實現依賴反轉。通過 BuildInjector 注入器來初始化所有的外部依賴。

4.1 基礎使用

dal 偽代碼如下:

func NewProjectDal(db *gorm.DB) *ProjectDal{
return &ProjectDal{
DB:db
}
}

type ProjectDal struct {
DB *gorm.DB
}

func (dal *ProjectDal) Create(ctx context.Context, item *entity.Project) error {
result := dal.DB.Create(item)
return errors.WithStack(result.Error)
}
// QuestionDal、QuestionModelDal...

service 偽代碼如下:

func NewProjectService(projectDal *dal.ProjectDal, questionDal *dal.QuestionDal, questionModelDal *dal.QuestionModelDal) *ProjectService {
return &projectService{
ProjectDal: projectDal,
QuestionDal: questionDal,
QuestionModelDal: questionModelDal,
}
}

type ProjectService struct {
ProjectDal *dal.ProjectDal
QuestionDal *dal.QuestionDal
QuestionModelDal *dal.QuestionModelDal
}

func (s *ProjectService) Create(ctx context.Context, projectBo *bo.ProjectCreateBo) (int64, error) {}

handler 偽代碼如下:

func NewProjectHandler(srv *service.ProjectService) *ProjectHandler{
return &ProjectHandler{
ProjectService: srv
}
}

type ProjectHandler struct {
ProjectService *service.ProjectService
}

func (s *ProjectHandler) CreateProject(ctx context.Context, req *project.CreateProjectRequest) (resp *
project.CreateProjectResponse, err error) {}

injector.go 偽代碼如下:

func NewInjector()(handler *handler.ProjectHandler) *Injector{
return &Injector{
ProjectHandler: handler
}
}

type Injector struct {
ProjectHandler *handler.ProjectHandler
// components,others...
}

在 wire.go 中如下定義:

// +build wireinject

package app

func BuildInjector() (*Injector, error) {
wire.Build(
NewInjector,

// handler
handler.NewProjectHandler,

// services
service.NewProjectService,
// 更多service...

//dal
dal.NewProjectDal,
dal.NewQuestionDal,
dal.NewQuestionModelDal,
// 更多dal...

// db
common.InitGormDB,
// other components...
)

return new(Injector), nil
}

執行 wire gen ./internal/app/wire.go 生成 wire_gen.go

// Code generated by Wire. DO NOT EDIT.

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

func BuildInjector() (*Injector, error) {
db, err := common.InitGormDB()
if err != nil {
return nil, err
}

projectDal := dal.NewProjectDal(db)
questionDal := dal.NewQuestionDal(db)
questionModelDal := dal.NewQuestionModelDal(db)
projectService := service.NewProjectService(projectDal, questionDal, questionModelDal)
projectHandler := handler.NewProjectHandler(projectService)
injector := NewInjector(projectHandler)
return injector, nil
}

在 main.go 中加入初始化 injector 的方法 app.BuildInjector

injector, err := BuildInjector()
if err != nil {
return nil, err
}

//project服務啟動
svr := projectservice.NewServer(injector.ProjectHandler, logOpt)
svr.Run()

注意,如果你運行時,出現了 BuildInjector? 重定義,那么檢查一下你的 //+build wireinject? 與 package app 這兩行之間是否有空行,這個空行必須要有!見https://github.com/google/wire/issues/117

4.2 高級特性

4.2.1 NewSet

NewSet? 一般應用在初始化對象比較多的情況下,減少 Injector? 里面的信息。當我們項目龐大到一定程度時,可以想象會出現非常多的 Providers。NewSet? 幫我們把這些 Providers 按照業務關系進行分組,組成 ProviderSet(構造器集合),后續只需要使用這個集合即可。

// project.go
var ProjectSet = wire.NewSet(NewProjectHandler, NewProjectService, NewProjectDal)

// wire.go
func BuildInjector() (*Injector, error) {
wire.Build(InitGormDB, ProjectSet, NewInjector)

return new(Injector), nil
}

4.2.2 Struct

上述例子的 Provider? 都是函數,除函數外,結構體也可以充當 Provider? 的角色。Wire 給我們提供了結構構造器(Struct Provider)。結構構造器創建某個類型的結構,然后用參數或調用其它構造器填充它的字段。

// project_service.go
// 函數provider
func NewProjectService(projectDal *dal.ProjectDal, questionDal *dal.QuestionDal, questionModelDal *dal.QuestionModelDal) *ProjectService {
return &projectService{
ProjectDal: projectDal,
QuestionDal: questionDal,
QuestionModelDal: questionModelDal,
}
}

// 等價于
wire.Struct(new(ProjectService), "*") // "*"代表全部字段注入

// 也等價于
wire.Struct(new(ProjectService), "ProjectDal", "QuestionDal", "QuestionModelDal")

// 如果個別屬性不想被注入,那么可以修改 struct 定義:
type App struct {
Foo *Foo
Bar *Bar
NoInject int `wire:"-"`
}

4.2.3 Bind

Bind? 函數的作用是為了讓接口類型的依賴參與 Wire? 的構建。Wire? 的構建依靠參數類型,接口類型是不支持的。Bind 函數通過將接口類型和實現類型綁定,來達到依賴注入的目的。

// project_dal.go
type IProjectDal interface {
Create(ctx context.Context, item *entity.Project) (err error)
// ...
}

type ProjectDal struct {
DB *gorm.DB
}

var bind = wire.Bind(new(IProjectDal), new(*ProjectDal))

4.2.4 CleanUp

構造器可以提供一個清理函數(cleanup),如果后續的構造器返回失敗,前面構造器返回的清理函數都會調用。初始化 Injector? 之后可以獲取到這個清理函數,清理函數典型的應用場景是文件資源和網絡連接資源。清理函數通常作為第二返回值,參數類型為 func()?。當 Provider? 中的任何一個擁有清理函數,Injector? 的函數返回值中也必須包含該函數。并且 Wire? 對 Provider 的返回值個數及順序有以下限制:

  1. 第一個返回值是需要生成的對象
  2. 如果有 2 個返回值,第二個返回值必須是 func() 或 error
  3. 如果有 3 個返回值,第二個返回值必須是 func(),而第三個返回值必須是 error
// db.go
func InitGormDB()(*gorm.DB, func(), error) {
// 初始化db鏈接
// ...
cleanFunc := func(){
db.Close()
}

return db, cleanFunc, nil
}

// wire.go
func BuildInjector() (*Injector, func(), error) {
wire.Build(
common.InitGormDB,
// ...
NewInjector
)

return new(Injector), nil, nil
}

// 生成的wire_gen.go
func BuildInjector() (*Injector, func(), error) {
db, cleanup, err := common.InitGormDB()
// ...
return injector, func(){
// 所有provider的清理函數都會在這里
cleanup()
}, nil
}

// main.go
injector, cleanFunc, err := app.BuildInjector()
defer cleanFunc()

更多用法具體可以參考 wire官方指南:https://github.com/google/wire/blob/main/docs/guide.md

4.3 高階使用

接著我們就用上述的這些 wire? 高級特性對 project 服務進行代碼改造:

project_dal.go

type IProjectDal interface {
Create(ctx context.Context, item *entity.Project) (err error)
// ...
}

type ProjectDal struct {
DB *gorm.DB
}

// wire.Struct方法是wire提供的構造器,"*"代表為所有字段注入值,在這里可以用"DB"代替
// wire.Bind方法把接口和實現綁定起來
var ProjectSet = wire.NewSet(
wire.Struct(new(ProjectDal), "*"),
wire.Bind(new(IProjectDal), new(*ProjectDal)))


func (dal *ProjectDal) Create(ctx context.Context, item *entity.Project) error {}
dal.go
// DalSet dal注入
var DalSet = wire.NewSet(
ProjectSet,
// QuestionDalSet、QuestionModelDalSet...
)

project_service.go

type IProjectService interface {
Create(ctx context.Context, projectBo *bo.CreateProjectBo) (int64, error)
// ...
}

type ProjectService struct {
ProjectDal dal.IProjectDal
QuestionDal dal.IQuestionDal
QuestionModelDal dal.IQuestionModelDal

}
func (s *ProjectService) Create(ctx context.Context, projectBo *bo.ProjectCreateBo) (int64, error) {}

var ProjectSet = wire.NewSet(
wire.Struct(new(ProjectService), "*"),
wire.Bind(new(IProjectService), new(*ProjectService)))

service.go

// ServiceSet service注入
var ServiceSet = wire.NewSet(
ProjectSet,
// other service set...
)

handler 偽代碼如下:

var ProjectHandlerSet = wire.NewSet(wire.Struct(new(ProjectHandler), "*"))

type ProjectHandler struct {
ProjectService service.IProjectService
}

func (s *ProjectHandler) CreateProject(ctx context.Context, req *project.CreateProjectRequest) (resp *
project.CreateProjectResponse, err error) {}

injector.go 偽代碼如下:

var InjectorSet = wire.NewSet(wire.Struct(new(Injector), "*"))

type Injector struct {
ProjectHandler *handler.ProjectHandler
// others...
}

wire.go

// +build wireinject

package app


func BuildInjector() (*Injector, func(), error) {
wire.Build(
// db
common.InitGormDB,
// dal
dal.DalSet,
// services
service.ServiceSet,
// handler
handler.ProjectHandlerSet,
// injector
InjectorSet,
// other components...
)

return new(Injector), nil, nil
}

5. 注意事項

5.1 相同類型問題

wire 不允許不同的注入對象擁有相同的類型。google 官方認為這種情況,是設計上的缺陷。這種情況下,可以通過類型別名來將對象的類型進行區分。

例如服務會同時操作兩個 Redis 實例,RedisA & RedisB

func NewRedisA() *goredis.Client {...}
func NewRedisB() *goredis.Client {...}

對于這種情況,wire 無法推導依賴的關系。可以這樣進行實現:

type RedisCliA *goredis.Client
type RedisCliB *goredis.Client

func NewRedisA() RedicCliA {...}
func NewRedisB() RedicCliB {...}

5.2 單例問題

依賴注入的本質是用單例來綁定接口和實現接口對象間的映射關系。而通常實踐中不可避免的有些對象是有狀態的,同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。針對這種場景我們通常設計多層的 DI 容器來實現單例隔離,亦或是脫離 DI 容器自行管理對象的生命周期。

6. 結語

Wire 是一個強大的依賴注入工具。與 Inject 、Dig 等不同的是,Wire只生成代碼而不是使用反射在運行時注入,不用擔心會有性能損耗。項目工程化過程中,Wire 可以很好協助我們完成復雜對象的構建組裝。

更多關于 Wire 的介紹請傳送至:https://github.com/google/wire

責任編輯:未麗燕 來源: 字節跳動技術團隊
相關推薦

2024-05-27 00:13:27

Go語言框架

2024-05-06 13:34:28

WireGoogleGo

2024-04-01 00:02:56

Go語言代碼

2024-02-01 13:30:53

Go語言開發

2021-07-07 10:48:00

DigGoWire

2023-12-09 14:29:30

編程語言Go

2019-05-08 14:37:49

Web服務器HTTP

2019-01-22 15:32:05

Go語言工具開發

2019-04-26 09:37:30

Go 開源技術

2011-05-31 10:00:21

Android Spring 依賴注入

2024-02-26 00:02:00

開發Go

2015-08-14 09:21:09

gdb工具調試 Go

2023-07-11 09:14:12

Beanquarkus

2021-10-11 06:38:52

Go開源庫語言

2024-10-28 00:40:49

Go語法版本

2022-06-05 23:30:25

AES加密算法

2017-08-16 16:00:05

PHPcontainer依賴注入

2022-12-29 08:54:53

依賴注入JavaScript

2025-10-31 02:30:00

Go系統Protobuf

2011-10-20 14:40:18

PHP
點贊
收藏

51CTO技術棧公眾號

久久天天躁狠狠躁夜夜躁| 色综合色狠狠天天综合色| 国产精品magnet| 久久久av毛片精品| 成人伊人精品色xxxx视频| 婷婷激情四射网| 欧美18免费视频| 欧美精品色一区二区三区| 岛国大片在线播放| 1769视频在线播放免费观看| 成人性生交大片免费| 国产精品美女久久久久久免费| 国产精品成人免费观看| 欧美日韩一二| 亚洲精品aⅴ中文字幕乱码| 亚洲欧美自拍另类日韩| 黄色视屏在线免费观看| 中文字幕一区二区日韩精品绯色| 国产综合动作在线观看| 99这里有精品视频| 日韩经典中文字幕一区| 久久久免费精品视频| 神马久久久久久久久久久| 97色成人综合网站| 欧美日韩国产一级片| 国产在线青青草| 女子免费在线观看视频www| 中文字幕乱码久久午夜不卡| 国产一区二区视频在线免费观看| 91国偷自产中文字幕久久| 国产精品主播| 久久久久国产精品一区| aa日韩免费精品视频一| 欧美日韩在线免费观看视频| 成人激情四射网| 久久成人综合网| 国产成人精品日本亚洲| 日韩xxx高潮hd| 欧美日韩午夜| 欧美成人午夜影院| 在线观看黄网址| 色狮一区二区三区四区视频| 亚洲区一区二区| 亚洲精品成人无码熟妇在线| 国产毛片精品| 亚洲第一色在线| 美女露出粉嫩尿囗让男人桶| 高清精品久久| 日韩一区二区在线观看视频| 日韩成人精品视频在线观看| 久久亚洲精品人成综合网| 91国偷自产一区二区开放时间| www.爱色av.com| 色偷偷偷在线视频播放| 精品露脸国产偷人在视频| 成年女人18级毛片毛片免费| 男插女视频久久久| 亚洲午夜羞羞片| 国产成人一区二区三区别| 国产精品刘玥久久一区| 亚洲丝袜制服诱惑| www.99riav| 久久免费电影| 黑人巨大精品欧美一区二区免费| 男女猛烈激情xx00免费视频| av中文资源在线资源免费观看| 亚洲一区二区三区视频在线| 欧美一级欧美一级| 日本在线播放一二三区| 欧美性猛交xxxx富婆弯腰| 国产免费黄色av| 成人免费福利| 欧美日韩国产综合一区二区| 日韩久久久久久久久久久| 亚洲精品一二三**| 亚洲跨种族黑人xxx| 国产黄片一区二区三区| 国产亚洲一区二区三区不卡| 最近2019好看的中文字幕免费| 香蕉久久久久久久| 欧美一区亚洲| 2019中文在线观看| 亚洲自拍偷拍另类| 成人一道本在线| 日本视频精品一区| h片在线播放| 欧美日韩国内自拍| 久热在线视频观看| 国产人妖ts一区二区| 一区二区欧美亚洲| 欧美成人三级在线观看| 国产精品五区| 91在线色戒在线| 天天干天天爱天天操| 欧美国产一区视频在线观看| 黄色一级片黄色| 国模一区二区| 亚洲第一天堂av| 大胸美女被爆操| 亚洲精品日本| 国产日韩欧美日韩大片| 性欧美videos另类hd| 久久久国产综合精品女国产盗摄| 熟女熟妇伦久久影院毛片一区二区| 91精品国产黑色瑜伽裤| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 中文字幕第10页| 狠狠做六月爱婷婷综合aⅴ| 欧美巨猛xxxx猛交黑人97人| 国产免费a视频| 高清视频一区二区| 亚洲黄色一区二区三区| 91超碰国产在线| 在线播放/欧美激情| 美女又爽又黄视频毛茸茸| 911久久香蕉国产线看观看| 日韩av免费在线看| 天天操天天插天天射| 日韩一区在线免费观看| 国产极品美女高潮无套久久久| 日本免费精品| 中文字幕日韩专区| 日日夜夜狠狠操| av高清不卡在线| 欧洲精品视频在线| 电影一区中文字幕| 国产亚洲精品va在线观看| 五月婷婷激情网| 国产福利一区二区三区视频 | 五月婷婷免费视频| 亚洲男同性视频| 在线看免费毛片| 欧美日韩亚洲在线观看| 国产成人一区三区| 四虎影视在线观看2413| 亚洲成av人片www| 中文字幕无人区二| 欧美日韩99| 51国偷自产一区二区三区的来源| 麻豆系列在线观看| 欧美裸体一区二区三区| 色欲狠狠躁天天躁无码中文字幕| 欧美亚洲自偷自偷| 久久大香伊蕉在人线观看热2| 欧美大胆的人体xxxx| 日韩一区二区三区观看| 蜜臀久久精品久久久用户群体| 久久精品二区亚洲w码| 亚洲欧洲久久| 四虎在线精品| 久久久av网站| 亚洲高清视频在线播放| 亚洲尤物在线视频观看| 欧美成人精品一区二区综合免费| 欧美日本一区二区视频在线观看 | 久久久这里只有精品视频| 亚洲AV无码国产精品午夜字幕| 亚洲精品国产无天堂网2021| 在线观看一区二区三区视频| 欧美日本中文| 狠狠色噜噜狠狠色综合久| 妞干网免费在线视频| 日韩高清a**址| 一级黄色av片| 中文字幕精品一区二区精品绿巨人 | 99se视频在线观看| 欧美精品aⅴ在线视频| 久草综合在线视频| www.亚洲色图| 日韩在线第三页| 欧美疯狂party性派对| 51午夜精品| 蜜桃麻豆影像在线观看| 亚洲一区第一页| 国产精品国产一区二区三区四区 | 99精品欧美一区二区三区小说 | 亚洲午夜色婷婷在线| 在线观看一二三区| 一区二区三区在线视频免费 | 精品女同一区二区三区| 亚洲一区二区三区在线播放| 91玉足脚交白嫩脚丫| 天使萌一区二区三区免费观看| 丝袜美腿玉足3d专区一区| 国产一区二区三区免费在线| 韩剧1988免费观看全集| 黄色片免费在线| 欧美亚洲国产一区二区三区| 免费在线观看成年人视频| 视频一区视频二区在线观看| 一区二区三区四区国产| 高清日韩中文字幕| 国产精品美女久久| 19禁羞羞电影院在线观看| 这里只有精品丝袜| 蜜桃视频污在线观看| 欧美色倩网站大全免费| 九九九国产视频| 国产精品午夜春色av| 涩视频在线观看| 日韩高清国产一区在线| 激情六月天婷婷| 不卡视频在线| 国产日韩一区二区| 日韩成人综合网| 78色国产精品| 在线heyzo| 正在播放亚洲1区| 天堂国产一区二区三区| 欧美日韩另类国产亚洲欧美一级| 国产精品成人免费一区二区视频| 国产精品色噜噜| 久久午夜夜伦鲁鲁片| 极品少妇一区二区三区精品视频| 日韩少妇内射免费播放| 中文字幕一区二区三区在线视频 | 在线一区二区视频| 久久一级黄色片| 《视频一区视频二区| 无码国产69精品久久久久同性| 成人a区在线观看| 天堂在线精品视频| 精品在线免费视频| 超碰在线97免费| 久久青草久久| 欧美二区在线视频| 亚洲精品1区2区| 黄网站色视频免费观看| 国产一区二区在线| 欧美一区观看| 中文字幕av一区二区三区人| 国模精品一区二区三区| 国产91精品入| 丁香婷婷久久久综合精品国产| 在线成人免费| 成人免费大片黄在线播放| 国产精品诱惑| 国产一区二区色| 久久电影tv| 国产aaa精品| 国产精品毛片久久久久久久久久99999999| 96精品视频在线| 欧美13videosex性极品| 91精品国产高清| h片在线观看下载| 羞羞色国产精品| 日本在线影院| 日韩女优在线播放| 欧美aaa大片视频一二区| 国产第一区电影| 久久久免费人体| 91免费版网站入口| 欧美日韩国产一区二区在线观看| 91亚洲va在线va天堂va国| 久久一级大片| 国产精品日韩高清| 欧美一区二区三区红桃小说| 蜜桃视频日韩| 成人激情电影在线| 中文字幕精品一区日韩| 中文字幕免费一区二区三区| 日韩视频 中文字幕| 亚洲国产精品第一区二区三区| www.av毛片| 久久午夜视频| 九九精品久久久| 国产成人综合网站| 日本护士做爰视频| 国产日韩精品一区二区三区| 国产亚洲精品久久久久久豆腐| 亚洲精选视频在线| 福利一区二区三区四区| 日本精品免费观看高清观看| 中文在线字幕av| 日韩精品一区二区三区在线 | 色综久久综合桃花网| 国产网站在线免费观看| 欧美精品国产精品日韩精品| 惠美惠精品网| 亚洲最大成人免费视频| 国产精品99久久免费观看| 欧美日韩在线精品| 91精品国产福利在线观看麻豆| 很污的网站在线观看| 日本不卡123| 伊人久久一区二区三区| 久久久国际精品| 欧美日韩精品一区二区三区视频播放| 黄色成人av网| 91成人国产综合久久精品| 亚洲成年人在线| 五月香视频在线观看| 国模gogo一区二区大胆私拍| 高清av一区二区三区| 成人区精品一区二区| 欧美一区二区三| 精品无码国产一区二区三区av| 男女性色大片免费观看一区二区| 性一交一黄一片| 国产精品美女视频| 久久精品国产成人av| 6080yy午夜一二三区久久| 日韩精品系列| 色综合久久88| 久久人人视频| 蜜桃av色综合| 国产综合色产| 亚洲欧美日韩精品一区| 久久蜜桃香蕉精品一区二区三区| 一区二区三区四区五区| 色94色欧美sute亚洲线路一ni| 国内精品久久久久久久久久久| 国产亚洲精品一区二555| 国产第一页在线视频| 成人春色激情网| 成人激情视频| 任你操这里只有精品| 成人av电影在线网| 欧美成人aaa片一区国产精品| 欧美三级电影在线观看| 日本一二三区在线视频| 欧美激情图片区| 国产精区一区二区| 在线观看成人av电影| 久久精品日产第一区二区| 国产精品一区二区人妻喷水| 亚洲精品日产精品乱码不卡| 中文在线资源天堂| 国产午夜精品视频免费不卡69堂| 国产乱码午夜在线视频 | 免费看av毛片| 色综合男人天堂| 久久在线观看| 99中文字幕在线观看| 久久99精品久久久久久国产越南| 亚洲ⅴ国产v天堂a无码二区| 日本二三区不卡| 你懂的在线视频| 欧美国产在线电影| 6080成人| 少妇高潮毛片色欲ava片| 成人福利在线看| 日韩精品视频播放| 日韩成人av网址| 在线观看涩涩| 欧美亚洲免费在线| 久久狠狠婷婷| 最近中文字幕免费| 欧美熟乱第一页| 理论片午午伦夜理片在线播放| 国产欧美精品一区二区三区-老狼 国产欧美精品一区二区三区介绍 国产欧美精品一区二区 | 欧美激情一区二区三区久久久| 这里视频有精品| 老太脱裤让老头玩ⅹxxxx| 北条麻妃国产九九精品视频| 国产成人无码精品| 亚洲精品天天看| 香蕉成人av| 亚洲一区三区电影在线观看| 久热成人在线视频| 一级黄色录像视频| 亚洲精品一线二线三线| 看黄在线观看| 色爱区成人综合网| 国产在线精品视频| 国产小视频在线观看免费| 亚洲丁香婷深爱综合| 中文不卡1区2区3区| 日日夜夜精品网站| 国模无码大尺度一区二区三区| 欧美日韩在线观看免费| 日韩大片免费观看视频播放| 美女网站视频一区| 日韩不卡视频一区二区| www.视频一区| 艳妇乳肉豪妇荡乳av无码福利 | 免费看污久久久| 免费观看在线综合| 久草网在线观看| 亚洲欧美日韩精品| 成人短视频软件网站大全app| 999久久欧美人妻一区二区| av一二三不卡影片| 在线观看免费高清视频| 久久久久久久久久久久av| 欧美人妖在线| 亚洲国产欧美91| 欧美日韩在线看| 国产午夜精品久久久久免费视| 久久久久久九九九九| 美女尤物国产一区| 97免费在线观看视频| 色噜噜久久综合伊人一本| 精品欠久久久中文字幕加勒比| 国产免费又粗又猛又爽| 性做久久久久久久免费看| 欧美日韩在线看片| 久久99精品久久久久久久久久| 狠狠色2019综合网| 性色av免费观看|