手撸golang 仿spring ioc/aop 之11 增强2

缘起

最近阅读 [Spring Boot技术内幕: 架构设计与实现原理] (朱智胜 , 2020.6)
本系列笔记拟采用golang练习之
Talk is cheap, show me the code.

Spring

Spring的主要特性:
1. 控制反转(Inversion of Control, IoC)
2. 面向容器
3. 面向切面(AspectOriented Programming, AOP)

源码gitee地址:
https://gitee.com/ioly/learning.gooop

原文链接:
https://my.oschina.net/ioly

目标

  • 参考spring boot常用注解,使用golang编写“基于注解的静态代码增强器/生成器”

子目标(Day 11)

  • 编写针对@RestController的增强器
    • enhancer/IEnhancer.go: 定义增强器接口
    • enhancer/RestControllerEnhancer.go:REST控制器的增强实现

enhancer/IEnhancer.go:
定义增强器接口

package enhancer

import "learning/gooop/spring/autogen/domain"

// IEnhancer clones the original file and appends enhanced code
type IEnhancer interface {
    Matches(file *domain.CodeFileInfo) bool
    Enhance(file *domain.CodeFileInfo) (error, *domain.CodeFileInfo)
}

enhancer/RestControllerEnhancer.go
REST控制器的增强实现

package controller

import (
    "errors"
    "fmt"
    "learning/gooop/spring/autogen/domain"
    "path"
    "strings"
)

type RestControllerEnhancer int

type tMappingMethodInfo struct {
    httpMethod string
    httpPath   string
    method     *domain.MethodInfo
}

func (me *RestControllerEnhancer) Matches(file *domain.CodeFileInfo) bool {
    for _, it := range file.Structs {
        if ok, _ := me.hasAnnotation(it.Annotations, "RestController"); ok {
            return true
        }
    }
    return false
}

func (me *RestControllerEnhancer) Enhance(file *domain.CodeFileInfo) (error, *domain.CodeFileInfo) {
    // clone file
    file = file.Clone().(*domain.CodeFileInfo)
    file.LocalFile = strings.Replace(file.LocalFile, ".go", "_Enhanced.go", -1)

    // find structs with @RestController
    changed := false
    for _, it := range file.Structs {
        if ok, a := me.hasAnnotation(it.Annotations, "RestController"); ok {
            // enhance target struct
            e := me.enhanceStruct(it, a)
            if e != nil {
                return e, nil
            }
            changed = true
        }
    }

    if changed {
        return nil, file
    } else {
        return nil, nil
    }
}

func (me *RestControllerEnhancer) hasAnnotation(arr []*domain.AnnotationInfo, name string) (bool, *domain.AnnotationInfo) {
    for _, it := range arr {
        if it.Name == name {
            return true, it
        }
    }

    return false, nil
}

func (me *RestControllerEnhancer) enhanceStruct(s *domain.StructInfo, sa *domain.AnnotationInfo) error {
    // update struct name
    s.Name = s.Name + "_Enhanced"

    // ensure imports
    me.ensureImport(s.CodeFile, "github.com/gin-gonic/gin")
    me.ensureImport(s.CodeFile, "net/http")

    // enhance GetMapping methods
    methods := []*tMappingMethodInfo{}
    for _, it := range s.Methods {
        if ok, a := me.hasAnnotation(it.Annotations, "GetMapping"); ok {
            e := me.enhanceGetMapping(it, a)
            if e != nil {
                return e
            }

            info := new(tMappingMethodInfo)
            info.httpMethod = "GET"
            info.httpPath = path.Join(sa.Get("path"), a.Get("path"))
            info.method = it
            methods = append(methods, info)
        }
    }

    // enhance PostMapping methods
    for _, it := range s.Methods {
        if ok, a := me.hasAnnotation(it.Annotations, "PostMapping"); ok {
            e := me.enhancePostMapping(it, a)
            if e != nil {
                return e
            }

            info := new(tMappingMethodInfo)
            info.httpMethod = "POST"
            info.httpPath = path.Join(sa.Get("path"), a.Get("path"))
            info.method = it
            methods = append(methods, info)
        }
    }

    // generate RegisterRestController()
    if len(methods) <= 0 {
        return errors.New("no mapping method found")
    }
    file := s.CodeFile
    addLine := func(line string) {
        file.AdditionalLines = append(file.AdditionalLines, line)
    }
    addLine(`// RegisterRestController is auto generated to implements controller.IRestController interface`)
    addLine(fmt.Sprintf(`func (me *%s) RegisterRestController(r *gin.Engine) {`, s.Name))
    for _, it := range methods {
        addLine(fmt.Sprintf(`  r.%s("%s", me.%s)`, it.httpMethod, it.httpPath, it.method.Name))
    }
    addLine(`}`)

    return nil
}

func (me *RestControllerEnhancer) ensureImport(file *domain.CodeFileInfo, p string) {
    for _, it := range file.Imports {
        if it.Package == p {
            return
        }
    }

    // add import info
    it := new(domain.ImportInfo)
    it.CodeFile = file
    it.Package = p
    file.AdditionalImports = append(file.AdditionalImports, it)
}

func (me *RestControllerEnhancer) enhanceGetMapping(method *domain.MethodInfo, a *domain.AnnotationInfo) error {
    // todo: fixme
    panic("implements me")
}

func (me *RestControllerEnhancer) enhancePostMapping(method *domain.MethodInfo, a *domain.AnnotationInfo) error {
    // todo: fixme
    panic("implements me")
}

(未完待续)

你可能感兴趣的:(手撸golang 仿spring ioc/aop 之11 增强2)