没有比新增一些更酷的指令, 更让人兴奋的啦~
要办到它, 得从下面的类入手.
像env 这样不需要用到Instrumentation
指令, 可以直接继承Command
, 将自定义指令的逻辑实现在run
方法中.
切记, 指令只能有一个构造器, 因为只有第一个构造器会被用于实例化指令; 并且, 构造器应至少有一个参数是
PrintOut
, 用以Command
的初始化, 它是用来向控制台回显信息的. 不用担心如何构造一个PrintOut
的实例, 只用留好这个参数,HouseMD
会帮我们搞定.
典型的过程是:
Command
的具体实现类会被House引导给Duck并由Telephone来实例化它们,housemd>
提示下输入一串字符, 按下回车后, 这串字符会被拆分为为指令和其参数选项两个部分, 通过指令部分匹配相应指令的实例, 而参数选项的部分则交由指令实例的parse
方法进行解析,run
方法会被调用Command
提供了三种方法来实现声明:
flag
声明布尔类型的选项, 通常用于开启什么模式或特定的动作, 例如loaded
指令的-h
选项option
声明除布尔类型之外的单值选项, 选项是必须有默认值的, 例如trace
的-p
选项parameter
声明参数,
请确保参数选项的声明, 使用在声明属性的位置完成的, 目的是为了保证在执行Command
的parse
方法之前, 所有的声明已完成.
细节请参考代码作为示例:
Instrumentation
的Command
实现能做什么?运行在目标进程的环境中, 很多JVM
提供的工具方法就可以拿来了:
java.lang.System.getenv()
来实现查看目标进程的系统环境变量( 顺便提一下, 我刻意没有实现查看Properities
的功能, 留给大家练手用 :D )java.lang.management.ManagementFactory
获得系统提供的管理Bean, 来实现诸如: 查看线程数, 内存, 执行Full GC
等等总之, 发挥你的想象力, 指令可以运行在目标进程中了, 还有什么可以直接拿来用的呢?
若是要用到Instrumentation
的指令, 也同样要继承Command
, 与 env, 但构造器就要多一个Instrumentation
的参数, 如loaded. HouseMD
在实例化指令的时候, 会将Instrumentation
的实例传入.
指令的构造器最多就
Instrumentation
和PrintOut
两个参数, 多了会导致实例化失败.
要实现一个比trace更牛逼的指令, 前面的方法已经足够让你办到了. 但我还是强烈推荐继承TransformCommand
, 它已经帮你实现了字节码增强的部分(这部分是有很多陷阱的, 除非你有足够的经验, 真的如此我热切的希望你能帮我改进它), 还有常规选项(如-i
,-t
,-l
以及-p
) 剩下你需要做的是:
isCandidate
方法, 以过滤那些需要进行字节码增强的类,isDecorating
方法,以过滤那些需要进行字节码增强的类的方法,hook
方法, 返回一个回调对象, 它的方法会在特定的时机被调用:
enterWith
会在所跟踪的方法进入之前调用, Context
对象有方法上下文的数据exitWith
会在所跟踪的方法退出之后调用, Context
对象有方法上下文的数据heartbeat
会像心跳一样被调用(可能一会快,可能一会慢), 主要用于实时显示信息, 如trace
的调用摘要, now
是当前时间(毫秒)finalize
会在指令结束前调用, 主要用于释放清理资源, 一旦指令运行中出现了异常, Option[Throwable]
会传入它.细节请参考代码作为示例:
要想自定义的指令具备良好的交互体验, 那么支持参数自动补全这个功能一定要提供. 实现这点就需要实现Completer
接口的complete
方法, 具体怎么做, 文字不好描述, 还是看源码吧.
新增的指令需在House中agentOptions
的值(:: Nil
之前)增加一下, 现在方式有点土, 欢迎高手来改进它.
更多相关疑问请提交Issue, 我们一起来讨论:)
完整开发文档请见这里