手撸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_cmdimport ( "errors" "fmt" "learning/gooop/spring/autogen/command" "learning/gooop/spring/autogen/common" "os" "strings")// ProjectCmd defines a project with name and dirtype ProjectCmd struct { name string dir string}// ProjectCmdBuilder parses cli input and creates a ProjectCmd instancetype ProjectCmdBuilder intconst 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 commontype tChars intvar 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 commonimport ( "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 servicetype ICodingService interface { iCmdRunner State() iCodingState Start()}
service/iCodingContext.go
代码生成上下文
package service// iCodingContext provides context info for iCodingState instancetype iCodingContext interface { HandleStateChanged(state iCodingState)}
service/iCodingState.go
状态模式下的服务状态
package servicetype iCodingState interface { iCmdRunner}
service/iCmdRunner.go
定义指令执行器接口,具体执行拟走责任链模式以便扩大
package serviceimport "learning/gooop/spring/autogen/command"// iCmdRunner runs a user cmdtype iCmdRunner interface { Run(cmd command.ICmd) error}
service/tInitialState.go
默认的服务状态
package serviceimport ( "errors" "learning/gooop/spring/autogen/command")// tInitialState is the default state for a coding service// it will only accept ProjectCmdtype 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")}
(未完待续)