缘起

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

Spring

  1. Spring基于J2EE技术实现了一套轻量级的
  2. Java Web Service系统应用框架。
  3. 它有很多优秀的特性,很多公司都选择把
  4. Spring作为产品或项目的基础开发架构。
  5. Spring的特性包括轻量、控制反转
  6. Inversion of Control, IoC)、
  7. 面向容器、面向切面(AspectOriented
  8. Programming, AOP)和框架灵活等。
  9. 源码gitee地址:
  10. https://gitee.com/ioly/learning.gooop
  11. 原文链接:
  12. https://my.oschina.net/ioly

目标

  • 参考spring常用注解,使用golang编写“基于注解的静态代码增强器/生成器”
    • 配置: ComponentScan,Configuration, Bean
    • Bean声明:Component, Service, Controller
    • Bean注入:Autowried
    • AOP注解:Before, After, Around, PointCut

子目标(Day 2)

  • 构思app的运行模式:
    • 本地standlone模式运行
    • 提供基于cli的命令行实时交互
    • 生成旁路代码:只扫描源代码,不修改源代码,增强后的代码加统一后缀
  • 设计cli交互指令集:
    • config save:保存配置
    • config saveas :另存配置
    • watch add :添加代码扫描目录
    • watch del :移除代码扫描目录
    • watch list:显示当前扫描的代码目录集合
    • gen:生成增强代码,也就是扫描所有注解,并生成增强类和增强方法

设计

  • config/IConfiguration:配置接口
  • command/ICmd:指令接口
  • command/ICmdBuilder:指令构建器接口
  • command/ICmdContext:指令执行上下文接口
  • config_cmd/SaveCmd: 保存配置
  • config_cmd/SaveASCmd: 另存配置
  • watch_cmd/AddCmd: 添加监视
  • watch_cmd/DelCmd: 移除监视
  • watch_cmd/ListCmd: 显示已监视目录的列表
  • gen_cmd/GenCmd: 生成增强类和增强方法
  • model/IEventDrivenModel:“事件驱动”的逻辑编排模型
  • logger: 日志接口,略

config/IConfiguration.go

配置接口

  1. package config
  2. // IConfiguration defines system configuration interface
  3. type IConfiguration interface {
  4. GetProjectName() string
  5. SetProjectName(string)
  6. GetWatchPaths() []string
  7. AddWatchPath(string)
  8. DelWatchPath(string)
  9. Save() error
  10. SaveAS(string) error
  11. }

command/ICmd.go

指令接口

  1. package command
  2. import "fmt"
  3. // ICmd defines cli command interface
  4. type ICmd interface {
  5. fmt.Stringer
  6. // Apply apply current command into use
  7. Apply(ICmdContext) error
  8. }

command/ICmdBuilder.go

指令构建器接口

  1. package command
  2. // ICmdBuilder parse input string and create an ICmd instance
  3. type ICmdBuilder interface {
  4. Build(string) (error, ICmd)
  5. }

command/ICmdContext.go

指令执行上下文接口

  1. package command
  2. import "learning/gooop/spring/autogen/config"
  3. // ICmdContext provides context info for all commands
  4. type ICmdContext interface {
  5. GetConfiguration() config.IConfiguration
  6. }

config_cmd/SaveCmd.go

保存配置

  1. package config_cmd
  2. import (
  3. "learning/gooop/spring/autogen/command"
  4. )
  5. // SaveCmd calls service.Save() to save current configuration, in JSON format
  6. type SaveCmd int
  7. // SaveCmdBuilder parse cli input and build a SaveCmd instance
  8. type SaveCmdBuilder int
  9. const gSaveCmdString = "config save"
  10. var gSaveCmdInstance = new(SaveCmd)
  11. func (me *SaveCmd) String() string {
  12. return gSaveCmdString
  13. }
  14. func (me *SaveCmd) Apply(c command.ICmdContext) error {
  15. // todo: fixme
  16. panic("implements me")
  17. }
  18. func (me *SaveCmdBuilder) Build(line string) (error, command.ICmd) {
  19. if line != gSaveCmdString {
  20. return nil, nil
  21. }
  22. return nil, gSaveCmdInstance
  23. }

config_cmd/SaveASCmd.go

另存配置

  1. package config_cmd
  2. import (
  3. "errors"
  4. "learning/gooop/spring/autogen/command"
  5. "strings"
  6. )
  7. // SaveASCmd calls service.SaveAS() to save current config into specific file, in JSON format
  8. type SaveASCmd struct {
  9. file string
  10. }
  11. // SaveASCmdBuilder parse cli input and returns a SaveASCmd instance
  12. type SaveASCmdBuilder int
  13. const gSaveASCmdPrefix = "config saveas "
  14. func (me *SaveASCmd) String() string {
  15. return gSaveASCmdPrefix + me.file
  16. }
  17. func (me *SaveASCmd) Apply(c command.ICmdContext) error {
  18. // todo: fixme
  19. panic("implements me")
  20. }
  21. func (me *SaveASCmdBuilder) Build(line string) (error, command.ICmd) {
  22. if !strings.HasPrefix(line, gSaveASCmdPrefix) {
  23. return nil, nil
  24. }
  25. file := strings.TrimSpace(line[len(gSaveASCmdPrefix):])
  26. if len(file) <= 0 {
  27. return errors.New("empty file path"), nil
  28. }
  29. return nil, &SaveASCmd{file }
  30. }

watch_cmd/AddCmd.go

添加监视

  1. package watch_cmd
  2. import (
  3. "learning/gooop/spring/autogen/command"
  4. "os"
  5. "strings"
  6. )
  7. // AddCmd calls service.WatchAdd() to add dir to watch list
  8. type AddCmd struct {
  9. dir string
  10. }
  11. type AddCmdBuilder int
  12. var gAddCmdPrefix = "watch add "
  13. func (me *AddCmd) String() string {
  14. return gAddCmdPrefix + me.dir
  15. }
  16. func (me *AddCmd) Apply(c command.ICmdContext) error {
  17. // todo: fixme
  18. panic("implements me")
  19. }
  20. func (me *AddCmdBuilder) Build(line string) (error, command.ICmd) {
  21. // check prefix
  22. if !strings.HasPrefix(line, gAddCmdPrefix) {
  23. return nil, nil
  24. }
  25. // get dir
  26. dir := strings.TrimSpace(line[len(gAddCmdPrefix):])
  27. // check dir
  28. _,e := os.Stat(dir)
  29. if e != nil {
  30. return e, nil
  31. }
  32. // ok
  33. return nil, &AddCmd{dir }
  34. }

watch_cmd/DelCmd.go

移除监视

  1. package watch_cmd
  2. import (
  3. "learning/gooop/spring/autogen/command"
  4. "os"
  5. "strings"
  6. )
  7. // DelCmd calls service.WatchDel() to remove dir from watch list
  8. type DelCmd struct {
  9. dir string
  10. }
  11. type DelCmdBuilder int
  12. var gDelCmdPrefix = "watch del "
  13. func (me *DelCmd) String() string {
  14. return gDelCmdPrefix + me.dir
  15. }
  16. func (me *DelCmd) Apply(c command.ICmdContext) error {
  17. // todo: fixme
  18. panic("implements me")
  19. }
  20. func (me *DelCmdBuilder) Build(line string) (error, command.ICmd) {
  21. // check prefix
  22. if !strings.HasPrefix(line, gDelCmdPrefix) {
  23. return nil, nil
  24. }
  25. // get dir
  26. dir := strings.TrimSpace(line[len(gAddCmdPrefix):])
  27. // check dir
  28. _,e := os.Stat(dir)
  29. if e != nil {
  30. return e, nil
  31. }
  32. // ok
  33. return nil, &DelCmd{ dir }
  34. }

watch_cmd/ListCmd.go

显示已监视目录的列表

  1. package watch_cmd
  2. import (
  3. "learning/gooop/spring/autogen/command"
  4. )
  5. // ListCmd calls service.WatchList
  6. type ListCmd int
  7. // ListCmdBuilder parse cli input and try to build a ListCmd instance
  8. type ListCmdBuilder int
  9. const gListCmdString1 = "watch list"
  10. const gListCmdString2 = "watch ls"
  11. var gListCmdSingleton = new(ListCmd)
  12. func (me *ListCmd) String() string {
  13. return gListCmdString1
  14. }
  15. func (me *ListCmd) Apply(c command.ICmdContext) error {
  16. // todo:
  17. panic("implements me")
  18. }
  19. func (me *ListCmdBuilder) Build(line string) (error, command.ICmd) {
  20. if line != gListCmdString1 && line != gListCmdString2 {
  21. return nil, nil
  22. }
  23. return nil, gListCmdSingleton
  24. }

gen_cmd/GenCmd.go

生成增强类和增强方法

  1. package gen_cmd
  2. import (
  3. "learning/gooop/spring/autogen/command"
  4. )
  5. // GenCmd calls service.Gen() to generate enhanced code files at once
  6. type GenCmd int
  7. // GenCmdBuilder parse cli input and try to build a GenCmd instance
  8. type GenCmdBuilder int
  9. const gGenCmdString = "gen"
  10. var gGenCmdSingleton = new(GenCmd)
  11. func (me *GenCmd) String() string {
  12. return gGenCmdString
  13. }
  14. func (me *GenCmd) Apply(c command.ICmdContext) error {
  15. panic("implements me")
  16. }
  17. func (me *GenCmdBuilder) Build(line string) (error, command.ICmd) {
  18. if line != gGenCmdString {
  19. return nil, nil
  20. }
  21. return nil, gGenCmdSingleton
  22. }

model/IEventDrivenModel.go

“事件驱动”的逻辑编排模型

  1. package model
  2. // IEventDrivenModel defines an event driven model for code arrangement
  3. type IEventDrivenModel interface {
  4. Hook(e string, handleFunc TEventHandleFunc)
  5. Fire(e string, args ...interface{})
  6. FireAsync(e string, args ...interface{})
  7. }
  8. type TEventHandleFunc func(e string, args ...interface{})
  9. type TEventDrivenModel struct {
  10. items map[string][]TEventHandleFunc
  11. }
  12. func (me *TEventDrivenModel) Hook(e string, handler TEventHandleFunc) {
  13. if me.items == nil {
  14. me.items = make(map[string][]TEventHandleFunc)
  15. }
  16. arr, ok := me.items[e]
  17. if ok {
  18. me.items[e] = append(arr, handler)
  19. } else {
  20. me.items[e] = []TEventHandleFunc{handler}
  21. }
  22. }
  23. func (me *TEventDrivenModel) Fire(e string, args ...interface{}) {
  24. if handlers, ok := me.items[e]; ok {
  25. for _, it := range handlers {
  26. it(e, args...)
  27. }
  28. }
  29. }
  30. func (me *TEventDrivenModel) FireAsync(e string, args ...interface{}) {
  31. go me.Fire(e, args...)
  32. }

(未完待续)