Guice AOP生火指南

来源:互联网 发布:萧县先锋网通知通告 编辑:程序博客网 时间:2024/05/01 19:55

转载自: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
publicclass BizService {
 
    @NeedLog
    publicvoid doFoo(){
        System.out.println("do foo");
    }
 
    publicvoid doBiz(){
        System.out.println("do biz");
    }
 
    @NeedLog
    publicvoid doBar(){
        System.out.println("do bar");
    }
 
    @NeedLog
    publicvoid 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
publicclass LogInterceptor implementsMethodInterceptor {
    @Override
    publicObject invoke(MethodInvocation invocation) throwsThrowable {
        System.out.println("log something when " + invocation.getMethod().getName() + " invoked.");
        Object[] args = invocation.getArguments();
        for(Object arg : args){
            System.out.println("[Argument:"+ arg.toString() + "]");
        }
        returninvocation.proceed();
    }
}

接下来需要做一次绑定:

?
1
2
3
4
5
6
7
8
publicclass LogModule extendsAbstractModule {
    @Override
    protectedvoid configure() {
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(NeedLog.class),
                newLogInterceptor());
    }
 
}

bindInterceptor方法有两个Matchers的参数,Matcher是一个简单的接口,用来表示是否匹配;在Guice AOP中,需要两个Matcher:一个表示参与的类,另一个代表相关的方法。除any()外,Matchers还提供了如identicalTo、inPackage、inSubpackage等方法用来匹配,具体用法可参考这里。

最后来看一下调用的例子:

?
1
2
3
4
5
6
7
8
9
10
publicclass Program {
    publicstatic void main(String[] args) {
        Injector injector = Guice.createInjector(newLogModule());
        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之间提供了很好的可移植性。

原创粉丝点击