手撸golang 仿spring ioc/aop 之3

手撸golang 仿spring ioc/aop 之3

缘起

最近阅读 [Offer来了:Java面试核心知识点精讲(框架篇)] (王磊 , 2020.6)
本系列笔记拟采用golang练习之
Talk is cheap, show me the code.

Spring

Spring基于J2EE技术实现了一套轻量级的
Java Web Service系统应用框架。
它有很多优秀的特性,很多公司都选择把
Spring作为产品或项目的基础开发架构。

Spring的主要特性包括:
1. 轻量
2. 控制反转(Inversion of Control, IoC)
3. 面向容器
4. 面向切面(AspectOriented Programming, AOP)
5. 框架灵活

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

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

目标

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

    • Bean声明:Component, Service, Controller

    • Bean注入:Autowried

    • AOP注解:Before, After, Around, PointCut

子目标(Day 3)

  • 添加ProjectCmd以支持项目定义
  • 添加文本扫描的辅助类:Chars.go, Tokens.go
  • 定义代码生成服务接口ICodingService及其支撑接口

设计

  • command/ProjectCmd: 项目定义指令
  • common/Chars: 字符识别辅助类
  • common/Tokens: 组合文本识别辅助类
  • service/ICodingService: 代码生成服务接口
  • service/iCodingContext: 代码生成上下文
  • service/iCodingState:状态模式下的服务状态
  • service/iCmdRunner:定义指令执行器接口,具体执行拟走责任链模式以便扩展
  • service/tInitialState:默认的服务状态

command/ProjectCmd.go

项目定义指令

package project_cmd

import (
    "errors"
    "fmt"
    "learning/gooop/spring/autogen/command"
    "learning/gooop/spring/autogen/common"
    "os"
    "strings"
)

// ProjectCmd defines a project with name and dir
type ProjectCmd struct {
    name string
    dir string
}

// ProjectCmdBuilder parses cli input and creates a ProjectCmd instance
type ProjectCmdBuilder int

const gProjectCmdPrefix = "project "
var gErrorInvalidProjectCmd = errors.New("invalid project cmd")

func (me *ProjectCmd) String() string {
    return fmt.Sprintf("project %s %s", me.name, me.dir)
}

func (me *ProjectCmd) Apply(ctx command.ICmdContext) error {
    panic("implements me")
}

func (me *ProjectCmdBuilder) Build(line string) (error, command.ICmd) {
    if !common.Tokens.MatchString(line, gProjectCmdPrefix) {
        return nil, nil
    }

    line = strings.TrimSpace(line[len(gProjectCmdPrefix):])
    b,name := common.Tokens.MatchIdentifier(line)
    if !b {
        return gErrorInvalidProjectCmd, nil
    }

    line = line[len(name):]
    b, spaces := common.Tokens.MatchSpaces(line)
    if !b {
        return gErrorInvalidProjectCmd, nil
    }

    line = line[len(spaces):]
    b, dir := common.Tokens.MatchDir(line)
    if !b {
        return gErrorInvalidProjectCmd, nil
    }
    _,e := os.Stat(dir)
    if e != nil {
        return e, nil
    }

    return nil, &ProjectCmd{ name, dir }
}

common/Chars.go

字符识别辅助类

package common


type tChars int

var Chars = new(tChars)

func (me *tChars) IsSpace(it rune) bool {
    switch it {
    case ' ':
        return true
    case '\t':
        return true
    case '\r':
        return true
    case '\n':
        return true
    }

    return false
}


func (me *tChars) Is09(it rune) bool {
    return it >= '0' && it <= '9'
}

func (me *tChars) Is19(it rune) bool {
    return it >= '1' && it <= '9'
}

func (me *tChars) IsLetter(it rune) bool {
    return (it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z')
}

func (me *tChars) IsUnderscore(it rune) bool {
    return it == '_'
}

func (me *tChars) IsLB(it rune) bool {
    return it == '('
}

func (me *tChars) IsRB(it rune) bool {
    return it == ')'
}

func (me *tChars) IsChar(it rune, args... rune) bool {
    for _,v := range args {
        if v == it {
            return true
        }
    }
    return false
}

func (me *tChars) IsSQuote(it rune) bool {
    return me.IsChar(it, '\'')
}

func (me *tChars) IsDQuote(it rune) bool {
    return me.IsChar(it, '"')
}

func (me *tChars) IsRSplash(it rune) bool {
    return me.IsChar(it, '\\')
}

func (me *tChars) IsLSplash(it rune) bool {
    return me.IsChar(it, '/')
}

common/Tokens.go

组合文本识别辅助类

package common

import (
    "regexp"
    "strings"
    "sync"
)

type tTokens struct {
    cache map[string]*regexp.Regexp
    rwmutex *sync.RWMutex
}

var Tokens = newTokensLib()

func newTokensLib() *tTokens {
    it := new(tTokens)
    it.init()
    return it
}

func (me *tTokens) init() {
    me.cache = make(map[string]*regexp.Regexp)
    me.rwmutex = new(sync.RWMutex)
}

func (me *tTokens) MatchString(s string, p string) bool {
    return strings.HasPrefix(s, p)
}

func (me *tTokens) MatchRegexp(s string, p string) (bool, string) {
    me.rwmutex.RLock()
    r,ok := me.cache[p]
    me.rwmutex.RUnlock()

    if !ok {
        me.rwmutex.Lock()
        if r,ok = me.cache[p];!ok {
            r,_ = regexp.Compile(p)
        }
        me.rwmutex.Unlock()
    }

    if r == nil {
        return false, ""
    }

    if !r.MatchString(s) {
        return false, ""
    }

    return true, r.FindString(s)
}

func (me *tTokens) MatchIdentifier(s string) (bool, string) {
    return me.MatchRegexp(s, "^[_a-zA-Z]\\w{0,99}}")
}

func (me *tTokens) MatchSpaces(s string) (bool, string) {
    return me.MatchRegexp(s, "^\\s+")
}

func (me *tTokens) MatchDir(s string) (bool, string) {
    b,s := me.MatchRegexp(s, "^([a-zA-Z]\\:)?([\\\\/][^\\s/:*?<>|\\\"\\\\]+)+[\\/]?")
    if b {
        return b,s
    }

    b,s = me.MatchRegexp(s, "^\\\"([a-zA-Z]\\:)?([\\\\/][^/:*?<>|\\\"\\\\]+)+[\\/]?\\\"")
    if b {
        return b,s
    }

    b,s = me.MatchRegexp(s, "^'([a-zA-Z]\\:)?([\\\\/][^'/:*?<>|\\\"\\\\]+)+[\\/]?'")
    if b {
        return b,s
    }

    return false, ""
}

service/ICodingService.go

代码生成服务接口

package service

type ICodingService interface {
    iCmdRunner

    State() iCodingState
    Start()
}

service/iCodingContext.go

代码生成上下文

package service

// iCodingContext provides context info for iCodingState instance
type iCodingContext interface {
    HandleStateChanged(state iCodingState)
}

service/iCodingState.go

状态模式下的服务状态

package service

type iCodingState interface {
    iCmdRunner
}

service/iCmdRunner.go

定义指令执行器接口,具体执行拟走责任链模式以便扩展

package service

import "learning/gooop/spring/autogen/command"

// iCmdRunner runs a user cmd
type iCmdRunner interface {
    Run(cmd command.ICmd) error
}

service/tInitialState.go

默认的服务状态

package service

import (
    "errors"
    "learning/gooop/spring/autogen/command"
)

// tInitialState is the default state for a coding service
// it will only accept ProjectCmd
type tInitialState struct {
    context iCodingContext
}

func newInitialState(c iCodingContext) iCodingState {
    it := new(tInitialState)
    it.init(c)
    return it
}

func (me *tInitialState) init(c iCodingContext) {
    me.context = c
}

var gErrorProjectDefineRequired = errors.New("project not defined: project <name> <dir>")


func (me *tInitialState) Run(cmd command.ICmd) error {
    panic("implement me")
}

(未完待续)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容