用go-ioc实现基于文件解析的IOC
2021-08-31 本文已影响0人
EasyNetCN
go-ioc是本人写的一个基于go的开源项目,实现了基于go源文件解析,模板生成container代码的IOC功能。可以根据自己的需求自行扩展,来满足自己逻辑。
下面的例子使用go-ioc来生成简单的repository container,controller container和自定义比较复杂的service container。
configuration.go
package main
import (
"io/ioutil"
"gopkg.in/yaml.v3"
)
type Configuration struct {
Project string `yaml:"project"`
GenerateType string `yaml:"generate-type"`
}
func NewConfiguration() (*Configuration, error) {
conf := new(Configuration)
data, err := ioutil.ReadFile("config.yml")
if err != nil {
return conf, err
}
err = yaml.Unmarshal(data, &conf)
return conf, err
}
config.yml
project:
generate-type:
service.go
package main
import (
"strings"
"github.com/easynet-cn/go-ioc"
)
type Service struct {
ioc.StructInfo
}
func (s Service) NewFuncString() string {
sb := new(strings.Builder)
for _, arg := range s.Args {
if strings.HasPrefix(arg.TypeName, "repository.") {
sb.WriteString("repositoryContainer.")
sb.WriteString(arg.TypeName[len("repository."):])
sb.WriteString(",\n")
} else {
dotIndex := strings.LastIndex(arg.TypeName, ".")
if dotIndex > -1 {
str := arg.TypeName[strings.LastIndex(arg.TypeName, ".")+1:]
sb.WriteString(strings.ToLower(str[0:1]))
sb.WriteString(str[1:])
if strings.Contains(arg.TypeName, "[") && strings.Contains(arg.TypeName, "]") {
sb.WriteString("s")
}
sb.WriteString(",\n")
} else if _, ok := baseTypes[arg.TypeName]; ok {
sb.WriteString(arg.Name)
sb.WriteString(",\n")
} else {
sb.WriteString(strings.ToLower(arg.TypeName[0:1]))
sb.WriteString(arg.TypeName[1:])
sb.WriteString(",\n")
}
}
}
return sb.String()
}
service_context.go
package main
import (
"sort"
"strings"
)
type ServiceContext struct {
AllServices []Service
CitedServices []Service
Services []Service
}
func (c *ServiceContext) NewFuncString() string {
typeNames := make([]string, 0)
typeNameMap := make(map[string]string)
typeNameArgNameMap := make(map[string]string)
for _, s := range c.AllServices {
for _, arg := range s.Args {
typeName := arg.TypeName
if strings.HasPrefix(typeName, "repository.") {
typeName = "repository.RepositoryContainer"
}
if _, ok := typeNameMap[typeName]; !ok && strings.Contains(typeName, ".") {
typeNameMap[typeName] = typeName
typeNames = append(typeNames, typeName)
} else if _, ok := baseTypes[typeName]; ok {
if _, ok := typeNameMap[typeName]; !ok {
typeNameMap[typeName] = typeName
typeNames = append(typeNames, typeName)
typeNameArgNameMap[typeName] = arg.Name
}
}
}
}
sort.Strings(typeNames)
sb := new(strings.Builder)
for _, typeName := range typeNames {
if strings.HasPrefix(typeName, "repository.") {
sb.WriteString("repositoryContainer ")
sb.WriteString(typeName)
sb.WriteString(",\n")
} else {
dotIndex := strings.LastIndex(typeName, ".")
if dotIndex > -1 {
str := typeName[strings.LastIndex(typeName, ".")+1:]
sb.WriteString(strings.ToLower(str[0:1]))
sb.WriteString(str[1:])
if strings.Contains(typeName, "[") && strings.Contains(typeName, "]") {
sb.WriteString("s")
}
sb.WriteString(" ")
sb.WriteString(typeName)
sb.WriteString(",\n")
} else if argName, ok := typeNameArgNameMap[typeName]; ok {
sb.WriteString(argName)
sb.WriteString(" ")
sb.WriteString(typeName)
sb.WriteString(",\n")
} else {
sb.WriteString(strings.ToLower(typeName[0:1]))
sb.WriteString(typeName[1:])
sb.WriteString(" ")
sb.WriteString(typeName)
sb.WriteString(",\n")
}
}
}
return sb.String()
}
main.go
package main
import (
"fmt"
"text/template"
"github.com/easynet-cn/go-ioc"
)
func main() {
config, err := NewConfiguration()
if err != nil {
fmt.Println("can not read configuration file,err:", err)
return
}
generateTypes := make([]string, 0)
if config.GenerateType == "all" {
generateTypes = append(generateTypes, "controller", "repository", "service")
} else {
generateTypes = append(generateTypes, config.GenerateType)
}
for _, generateType := range generateTypes {
name, templateFile, outputFilename := containerNameAndOutputFilename(generateType)
containerConfig := ioc.ContainerConfig{
Name: name,
TemplateFile: templateFile,
InputDirectory: fmt.Sprintf("../%s/%s", config.Project, generateType),
OutputDirectory: fmt.Sprintf("../%s/%s", config.Project, generateType),
OutputFilename: outputFilename,
}
generator := ioc.NewGeneralContainerGenerator(containerConfig)
funcMap := template.FuncMap{"unescaped": ioc.Unescaped, "littleCamelCase": ioc.LittleCamelCase}
c := convert
if generateType == "service" {
c = serviceContextConvert
}
result, err := generator.Generate(c, funcMap)
fmt.Println(result, err)
}
}
func containerNameAndOutputFilename(generateType string) (string, string, string) {
if generateType == "controller" {
return "ControllerContainer", "controller-container.tpl", "controller_container.go"
} else if generateType == "repository" {
return "RepositoryContainer", "repository-container.tpl", "repository_container.go"
} else if generateType == "service" {
return "ServiceContainer", "service-container.tpl", "service_container.go"
}
return "", "", ""
}
var baseTypes = map[string]string{
"string": "string",
"*string": "*string",
"int": "int",
"*int": "*int",
"int64": "int64",
"*int64": "*int64",
}
func convert(structContext ioc.StructContext) interface{} {
return structContext
}
func serviceContextConvert(structContext ioc.StructContext) interface{} {
allServices := make([]Service, len(structContext.StructInfoes))
for i, structInfo := range structContext.StructInfoes {
allServices[i].StructInfo = structInfo
}
citedServiceMap := make(map[string]Service)
citedServices := make([]Service, 0)
for _, s := range allServices {
for _, s1 := range allServices {
if s.Name != s1.Name {
for _, arg := range s1.Args {
if _, ok := citedServiceMap[s.Name]; !ok && s.Name == arg.TypeName {
citedServiceMap[s.Name] = s
citedServices = append(citedServices, s)
}
}
}
}
}
processCitedService(&citedServices)
services := make([]Service, 0)
for _, s := range allServices {
if _, ok := citedServiceMap[s.Name]; !ok {
services = append(services, s)
}
}
return &ServiceContext{AllServices: allServices, CitedServices: citedServices, Services: services}
}
func processCitedService(citedServices *[]Service) {
for i, s1 := range *citedServices {
for j, s2 := range *citedServices {
if s1.Name != s2.Name {
for _, arg := range s2.Args {
if arg.TypeName == s1.Name {
if i > j {
(*citedServices)[i], (*citedServices)[j] = (*citedServices)[j], (*citedServices)[i]
processCitedService(citedServices)
}
}
}
}
}
}
}
controller-container.tpl
package controller
import (
"github.com/kataras/iris/v12/mvc"
)
// Count: {{len .StructInfoes}}
type ControllerContainer struct {
mvcApp *mvc.Application
serviceContainer *service.ServiceContainer
}
func NewControllerContainer(mvcApp *mvc.Application, serviceContainer *service.ServiceContainer) *ControllerContainer {
return &ControllerContainer{mvcApp: mvcApp, serviceContainer: serviceContainer}
}
func (c *ControllerContainer) InitMvc() {
{{range $controller := .StructInfoes -}}
c.mvcApp.Party({{.RequestMapping | unescaped}}).Configure(func(app *mvc.Application) {
{{range $prop := .Properties -}}
app.Register(c.serviceContainer.{{.Name}})
{{end -}}
app.Handle(new({{.Name}}))
})
{{end -}}
}
repository-container.tpl
package repository
import (
"github.com/kataras/golog"
"xorm.io/xorm"
)
// Count: {{len .StructInfoes}}
type RepositoryContainer struct{
{{range $repository := .StructInfoes -}}
{{.Name}} {{.Name}}
{{end -}}
}
func NewRepositoryContainer(engine *xorm.Engine, logger *golog.Logger) *RepositoryContainer{
return &RepositoryContainer{
{{range $repository := .StructInfoes -}}
{{.Name}}: New{{.Name}}(engine, logger),
{{end -}}
}
}
service-container.tpl
package service
import (
"github.com/kataras/golog"
"xorm.io/xorm"
)
// Count: {{len .AllServices}}
type ServiceContainer struct{
{{range $service := .CitedServices -}}
{{.Name}} {{.Name}}
{{end -}}
{{range $service := .Services -}}
{{.Name}} {{.Name}}
{{end -}}
}
func NewServiceContainer({{.NewFuncString}}) *ServiceContainer{
{{range $service := .CitedServices -}}
{{.Name | littleCamelCase}} := New{{.Name}}({{.NewFuncString}})
{{end -}}
return &ServiceContainer{
{{range $service := .CitedServices -}}
{{.Name}}: {{.Name | littleCamelCase}},
{{end -}}
{{range $service := .Services -}}
{{.Name}}: New{{.Name}}({{.NewFuncString}}),
{{end -}}
}
}