转载自:http://www.breakthen.com/2011/09/14/guice-aop-kick-start/
听说Guice的时候很早,但了解的不多,写过一些简单的例子,无非都是依赖注入(Dependency Injection)方面的,今天跟朋友聊起来Guice,就去看了看google code上的文档,发现Guice竟然也支持AOP,真是孤陋寡闻了。
看了看文档,发现用起来比较简单,下面举个简单的例子来说明。
Guice的AOP是通过借助Annotation实现的,在需要进行拦截的方法上加自定义的注解。
比如下面的一个注解NeedLog,表示在方法调用的时候需要记一下日志,代码如下:
1
2
3
4
|
@Retention
(RetentionPolicy.RUNTIME)
@Target
(ElementType.METHOD)
public
@interface
NeedLog {
}
|
这个注解是非常简单的,除了指明它作用在方法上,没有做任何事。
接下来定义业务类,BizService。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
BizService {
@NeedLog
public
void
doFoo(){
System.out.println(
"do foo"
);
}
public
void
doBiz(){
System.out.println(
"do biz"
);
}
@NeedLog
public
void
doBar(){
System.out.println(
"do bar"
);
}
@NeedLog
public
void
doSomethingWithParams(String param){
System.out.println(
"do something with params: "
+ param);
}
}
|
这个类定义了四个业务方法,其中doFoo, doBar, doSomethingWithParams使用了NeedLog注解,doBiz没有使用,另外,doSomethingWithParams这个方法还带了参数。
接下来需要实现一个MethodInterceptor,方法拦截器。它在被拦截的方法被调用的时候执行,以便有机会来获得被调用方法的上下文,包括方法名称,参数列表,参数值,然后代理给其它类来执行一些交叉的逻辑(cross-cutting logic,如日志、事务处理、验证授权等),最终,它有机会来判断返回值、抛出的异常或是简单的返回。拦截器可能会被应用于大量的方法并被多次调用,因此它们必须是高效的、非侵入的。
下面定义的LogInterceptor,简单的打印了一下被拦截的方法名,如果被拦截的方法有参数,将参数输出出来。代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
public
class
LogInterceptor
implements
MethodInterceptor {
@Override
public
Object invoke(MethodInvocation invocation)
throws
Throwable {
System.out.println(
"log something when "
+ invocation.getMethod().getName() +
" invoked."
);
Object[] args = invocation.getArguments();
for
(Object arg : args){
System.out.println(
"[Argument:"
+ arg.toString() +
"]"
);
}
return
invocation.proceed();
}
}
|
接下来需要做一次绑定:
1
2
3
4
5
6
7
8
|
public
class
LogModule
extends
AbstractModule {
@Override
protected
void
configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(NeedLog.
class
),
new
LogInterceptor());
}
}
|
bindInterceptor方法有两个Matchers的参数,Matcher是一个简单的接口,用来表示是否匹配;在Guice AOP中,需要两个Matcher:一个表示参与的类,另一个代表相关的方法。除any()外,Matchers还提供了如identicalTo、inPackage、inSubpackage等方法用来匹配,具体用法可参考这里。
最后来看一下调用的例子:
1
2
3
4
5
6
7
8
9
10
|
public
class
Program {
public
static
void
main(String[] args) {
Injector injector = Guice.createInjector(
new
LogModule());
BizService service = injector.getInstance(BizService.
class
);
service.doBiz();
service.doBar();
service.doFoo();
service.doSomethingWithParams(
"this is a param"
);
}
}
|
输出结果为:
1
2
3
4
5
6
7
8
|
do biz
log something when doBar invoked.
do bar
log something when doFoo invoked.
do foo
log something when doSomethingWithParams invoked.
[Argument:this is a param]
do something with params: this is a param
|
Guice的方法拦截也是通过动态生成字节码来实现的,通过创建子类重写相应的方法来实现拦截,不支持字节码生成的平台如Android就没法用了。
文档中还列出了被拦截的类与方法需要满足的条件:
1.类必须是public的或包内可见的;
2.类必须是非final的;
3.方法必须是public的、包内可见的或是protected的;
4.方法必须是非final的;
5.实例必须通过无参数的构造函数生成或者通过guice的@inject注解构造。
最后一点,guice实现的方法拦截API是AOP Alliance定义的公共规范的一部分,因此在不同framework之间提供了很好的可移植性。