昨天在答一个问题时,看题不清答错了,不过却让我花了点时间想如何实现简单的AOP。
在其它语言里实现AOP的确比较麻烦,java要用到动态proxy,如果是C++,除了从源码上修改还真没好办法,aspectc就是这么做的。那么ruby里面如何实现呢?
由于ruby是动态脚本语言,运行期可以把一个方法改名,也可以构造一个字符串动态生成方法,那么实现这个就不是难事了。
使用时只需要在类里面include这个模块就行了,相当于mixin的功能。
注意,由于使用execute_before时后面几个方法必须要有定义,所以必须放在后面,否则就会出错。
在其它语言里实现AOP的确比较麻烦,java要用到动态proxy,如果是C++,除了从源码上修改还真没好办法,aspectc就是这么做的。那么ruby里面如何实现呢?
由于ruby是动态脚本语言,运行期可以把一个方法改名,也可以构造一个字符串动态生成方法,那么实现这个就不是难事了。
module ExecuteBefore
def self . included(base)
base . extend(ClassMethods)
end
module ClassMethods
def execute_before(before_method , * methods)
methods . flatten . map ( &: to_sym) . each do | method |
alias_method " #{method}_old " . to_sym , method
class_eval <<- eval_end
def # {method}(*args)
#{before_method}(*args)
#{method}_old(*args)
end
eval_end
end
end
end
end
def self . included(base)
base . extend(ClassMethods)
end
module ClassMethods
def execute_before(before_method , * methods)
methods . flatten . map ( &: to_sym) . each do | method |
alias_method " #{method}_old " . to_sym , method
class_eval <<- eval_end
def # {method}(*args)
#{before_method}(*args)
#{method}_old(*args)
end
eval_end
end
end
end
end
使用时只需要在类里面include这个模块就行了,相当于mixin的功能。
class TestController
<
ApplicationController
def index
a( 1 )
b( 1 , 2 )
c( 1 , 2 , 3 )
render : text => ' hello '
end
protected
def log ( * args)
puts " log: #{args.map(&:to_s).join(', ')} "
end
def a(a)
end
def b(a , b)
end
def c(a , b , c)
end
include ExecuteBefore
execute_before : log , : a , : b , : c
end
def index
a( 1 )
b( 1 , 2 )
c( 1 , 2 , 3 )
render : text => ' hello '
end
protected
def log ( * args)
puts " log: #{args.map(&:to_s).join(', ')} "
end
def a(a)
end
def b(a , b)
end
def c(a , b , c)
end
include ExecuteBefore
execute_before : log , : a , : b , : c
end
注意,由于使用execute_before时后面几个方法必须要有定义,所以必须放在后面,否则就会出错。