美文网首页GO与微服务
手撸golang 仿spring ioc/aop 之11 增强2

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

作者: 老罗话编程 | 来源:发表于2021-04-20 00:01 被阅读0次

    缘起

    最近阅读 [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

        本文链接:https://www.haomeiwen.com/subject/dckglltx.html