Before using the go language of reflection to do some code generation, refer to this article.
But this way, the intrusion is too strong, need to execute the corresponding declaration call, so the automatic generation of Goa framework is very interested in, so carefully studied, found to use the more ingenious, here first sell a Xiaoguanzi, first look at the generated code directory structure.
Here we use Adder's Desgin file to generate:
Package Designimport (."github.com/goadesign/goa/design" . "GITHUB.COM/GOADESIGN/GOA/DESIGN/APIDSL")var_ = API ("Adder", func () {Title ("The adder API") Description ("A teaser for Goa") Host ("localhost:8080") Scheme ("http")})var_ = Resource ("operands", func () {Action ("Add", func () {Routing (GET ("Add/:left/:right")) Description ("add returns the sum of the parameters in the response body") Params (func () {Param (" Left", Integer,"Left operand") Param (" Right", Integer,"Right operand")}) Response (OK,"Text/plain") })})
Then generate the corresponding directory structure as follows (if you do not know how to generate, refer to the first article):
[Email protected]:~/gocode/src/goa-adder $tree. ├──app│├──contexts.go│├──controllers.go│├──hrefs.go│├── Media_types.go│├──test││└──operands.go│└──user_types.go├──client│├──adder-cli││├──com Mands.go││└──main.go│├──client.go│├──datatypes.go│└──operands.go├──design│└──design.go├──main.go├ ──operands.go└──swagger ├──swagger.json └──swagger.yaml
- App directory, generated framework-related code, routing with HTTP
- Client directory, generate a client test program for Go native request server for easy testing
- Swagger directory, generated by the swagger file, can be used swagger to describe the API, so as not to write their own API interface document (COOL)
- And then the Main.go, the main entrance to the program.
- Operands.go Business logic code, you need to modify it here
//Operands.goPackage Mainimport ("Github.com/goadesign/goa" "Goa-adder/app")//Operandscontroller implements the operands resource.Type Operandscontrollerstruct { *Goa. Controller}//Newoperandscontroller creates a operands controller.Func Newoperandscontroller (Service *goa. Service) *Operandscontroller {return&operandscontroller{controller:service. Newcontroller ("Operandscontroller")}}//Add runs the Add action.Func (c *operandscontroller) Add (CTX *app. Addoperandscontext) Error {//Tbd:implement write the corresponding function logic here returnNil}
Very good, no need to repeat the framework of the Low-level code (routing, codec, etc.).
Although the former company's framework was used before (that is, the use of Java's reflection to generate code automatically), but encountered the automatic generation of code this thing, still can't stop excited.
Here we don't study the generated framework code, first of all, how to use the Go language to automatically generate it.
General automatic generation can be divided into three steps:
1) Define services and interfaces by self-describing language (IDL,DSL OK)
2) Parse the description language, get the metadata (service name, interface name, interface parameter God horse)
3) Generate duplicate code sections based on metadata and templates corresponding to the framework
Let's see what Goa does, and with the--debug option in Goagen, you can keep the intermediate file.
// use command Goagen--debug bootstrap-d Goa-adder/design generate directory [Email protected]:~/gocode/src/goa-adder $tree-L 1 .├──app├──client├──design ├──goagen009966755├──goagen174102868├── goagen511141286├──goagen585483469 ├──main.go├──operands.go└──swagger
├──goagen009966755│├──goagen│└──main.go├──goagen174102868│├──goagen│└──main.go├──goagen5111412 86│├──goagen│└──main.go├──goagen585483469│├──goagen│└──main.go
We saw that a few more catalogs came in, and each directory contained a main.go and generated executable program, and we casually entered a directory to see:
//************************************************************************////Code Generator////Generated with Goagen v0.0.1, command line://$ Goagen//--debug bootstrap-d goa-adder/design////the content of this file is auto-generated, does not MODIFY//************************************************************************//Package Mainimport ("Github.com/goadesign/goa/goagen/gen_main" "FMT" "Strings""Github.com/goadesign/goa/dslengine" _ "Goa-adder/design") Func main () {//Check If there were errors while running the first DSL passDslengine. FailOnError (Dslengine. Errors)//Now run the secondary DSLsDslengine. FailOnError (Dslengine. Run ()) files, err: = Genmain. Generate () dslengine. FailOnError (ERR)//We ' re doneFmt. PRINTLN (Strings. Join (Files,"\ n"))}
And then we see some clues, it first put our design directory in the whole, and then call the engine inside the function, to generate code.
Here again go back to our DSL language to write the file Design.go
Package Designimport ( "github.com/goadesign/goa/design" " GITHUB.COM/GOADESIGN/GOA/DESIGN/APIDSL " var _ = API ("Adder", Func () { Title ("The Adder API") Description ("A teaser for Goa ") Host (" localhost:8080 ") Scheme (" http ")})
The API here, in fact, is in the call engine pre-defined functions, where the definition of it? See Source:
The Func API (namestring,DSL func ()) *design. apidefinition {ifDesign. Design.name! =""{dslengine. ReportError ("multiple API definitions, only one is allowed") returnNil}if!Dslengine. Istopleveldefinition () {dslengine. INCOMPATIBLEDSL ()returnNil}ifName = =""{dslengine. ReportError ("API name cannot be empty")} design. Design.name = name Design. Design.dslfunc = DSLreturndesign. Design}
The call to the API function generates the corresponding design instance and then saves the metadata (this is name and an anonymous function) in memory.
The design object is defined at the time the program is initialized (source code here), and the instance is registered to the build engine (in fact, the object instance is passed through for easy follow-up calls).
The Generate function is called later to generate the code automatically.
That's probably what this means, it's really ingenious, the DSL defines the anonymous global variables, the global variables are calls to the already defined metadata functions (e.g., APIs, etc.), and the DSL files are included in the package reference so that the metadata exists in the corresponding instance memory.
Then you can play casually, through the type of metadata, to generate the corresponding file, wonderful!
However, because of the support for various nesting, different types and fault tolerance, and so on, so the implementation of the code to write very much.
However, we can follow this idea to implement a simple example:
//Main.goPackage Mainimport"FMT"//defines the structure of the DSL language description to hold the data inside the DSLType Apidefinitionstruct { //Name of APINamestring //Title of APITitlestring //Description of APIsDescstring //Dslfunc contains the DSL used to create this definition if anydslfunc func ()}//implement DSL-corresponding APIs for instantiationThe Func API (namestring, DSL func ()) *apidefinition {API:=New(apidefinition) API. Name=The name API. Dslfunc=DSL//secretly assign a valueG_api =APIreturnAPI}//the corresponding title assignment valueFunc Title (valstring) { ifG_api! =Nil {G_api. Title=Val}} Func Description (dstring) { ifG_api! =Nil {G_api. Desc=D}}//an instance of the current design, where the global variables are used to indicatevarG_API *apidefinition//code generation based on in-memory storage datafunc generatetest () {//we need to execute the corresponding dslfunc here.G_api. Dslfunc () fmt. Println ("Get Name:", G_api. Name) fmt. Println ("Get Title:", G_api. Title) fmt. Println ("Get Desc:", G_api. DESC)}//Here's the DSL statement .var_ = API ("Adder", func () {Title ("The adder API") Description ("A teaser for Goa")}) Func main () {generatetest ()}
Finally run the result of the execution:
[Email protected]:~/gocode/auto-Gen $go run main.goget Name: adderget Title: The adder APIget Desc: for Goa
We've got the data that the user defined in the DSL (of course, the DSL description is written directly into the same file, eliminating the process of merging).
OK, the code of the automatic generation principle has been known, the following analysis framework of the overall structure and code.
[Goa]golang Micro-Service Framework Learning (ii)--code auto-generation