如何在Guice1.0中使用AOP

因为项目上需要使用Guice,而Guice并不像Spring那样提供一整套的解决方案(Spring是一个容器,而厌烦了容器的我选择了不属于容器的Guice1.0)。因此,在事务支持方面肯定不是依赖于Guice自己来实现。而我们都知道无论是日志还是事务,使用Aop的方案都是比较好的,而在学习过程中没有发现比较好的材料,于是通过官方的文档,还有Robbie Vanbrabant的书Apress.Google.Guice.Agile.Lightweight Dependency Injection Framework第四章的知识,总结出下面的内容。

 

其中代码示例来自于Robbie的原著,在这里相当于转载。

 

将aopalliance.jar放在Classpath下,我是与Guice-1.0.jar放在一起,程序就可以工作并使用AOP了。

 

1.Guice1.0如何实现Aop

Guice将使用代理的方式包装原来的类(我称之为目标类),为每一个目标类创建一个代理,通过代理实现Aop的功能,而创建的过程只需要通过在Module中绑定就可以完成,因此感觉不出代理类的创建。由于Guice的目标就是帮我们解决类的高效的创建问题,因此,对于代理类的创建应该也是很快的,因为还没有看过Guice的源代码,暂时不清楚其“快”是如何实现的。

 

2.Guice1.0的Aop的作用范围

因为还没有完全使用过Guice的所有功能,目前,我所知道的是Guice1.0仅支持对方法进行Aop操作。而Bob Lee自己也是Aop方面的专家,因此我感觉Guice的后续版本应该会支持更多更灵活的Aop操作。

 

3.Guice1.0是否可用

我看过Guice2.0的规划,与Guice1.0有98%的兼容,并且功能比Guice1.0更加全面,包括Aop的支持,而现在Guice1.0的功能已经足够充当一个完整的IoC框架了,因此现在Guice1.0已经进入了可用的状态。

 

4.Guice1.0在Module中对Aop的配置

void bindInterceptor(Matcher<? super Class<?>> classMatcher,
                     Matcher<? super Method> methodMatcher,
                     MethodInterceptor... interceptors) {...}

 这就是Guice中Binder类中用于进行Aop操作的类,通过配置”目标类“和”目标方法”,就可以配置一个或多个方法对“目标方法”进行拦截。

 

5.Guice1.0的Aop允许实现的操作

  • 在被拦截的方法前执行一些操作
  • 使用部分或完全不同的参数去调用被拦截的方法
  • 在被拦截的方法执行以后,去执行一些操作
  • 对被拦截的方法的返回值进行操作或者直接返回你想要的某个值
  • 直接屏闭被拦截的方法甚至直接在内部调用其他方法

6.MethodInvocation.proceed()直接跨越Aop拦截器

通过调用这个方法,可以直接获取“目标方法”真正的返回值而不被Aop的拦载方法干扰

 

好的,了解了这些知识以后,我们开始用一个例子讲解如何使用Guice1.0的Aop。例子程序使用Robbie的代码,在这里将顺序改变一下以适应于学习(以我个人的观点)

 

首先,我们先考虑一个问题,我们假定有一个类被创建后,应该有一个类去接收这个类执行的结果,但我们需要做另外两个类来拦截这个方法,其中一个类用于记录这个类要执行什么操作,第二个类将接收者替换成其他的对象。

 

现实的一个例子,你要打电话给Aunt Jane,但是FBI将会偷偷从中间截获你的电话,然后记录下你要播打的电话号码,然后还会将你的电话转给FBI的另一个人,让你以为已经接通了电话(但实际上你并不知道是否由Aunt Jane接收,因为电话已经被Aop过了,就是已经被拦截,记录了你播的号码并转给了另一个FBI特工)。

 

1.首先,创建“你”,就是电话的发出者,同时,也要创建电话的接收者对象。

    下面就是电话的播出者,通过HashMap作为电话本(可以理解为电话接线员)

import java.util.HashMap;
import java.util.Map;

public class Phone {
    private static final Map<Number, Receiver> RECEIVERS =
        new HashMap<Number, Receiver>();
    
    static { 
        RECEIVERS.put(123456789, new Receiver("Aunt Jane"));
        RECEIVERS.put(111111111, new Receiver("Santa"));
    }
    
    public Receiver call(Number number) {
        return RECEIVERS.get(number);
    }
}

     下面就是电话接收者对象:

public class Receiver {
    private final String name;
    public Receiver(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return String.format("%s[name=%s]", getClass().getName(), name);
    }
}

 2.开始转入“FBI”式操作,创建两个操作,这两个操作将被配置在Phone对象的call方法执行时,拦截这个操作,记录电话号码并将电话转给另一个人。这两个操作都要实现Guice的MethodInterceptor接口,因为这样Guice才能在注入的时候进行拦截。

    先是配置记录电话号码的类:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class PhoneLoggerInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        for (Object arg : invocation.getArguments())
            if (arg instanceof Number)
                System.out.println("CALL: "+arg);
        
        return invocation.proceed();
    }
}

     然后是执行转接电话操作的类:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class PhoneRedirectInterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return new Receiver("Alberto's Pizza Place");
    }
}

 3.下面是发起操作(进行打电话)

 

import com.google.inject.Guice;
import com.google.inject.Injector;

public class MakePhoneCall {
    public static void main(String[] args) {
        Injector i = Guice.createInjector(new PhoneModule());
        Phone phone = i.getInstance(Phone.class);
        Receiver auntJane = phone.call(123456789);
        System.out.println(auntJane);
    }
}

 4.但按照上面的做法并不会进行Aop操作,因为还没有按照Guice的“哲学”去进行Module的配置。下面是进行Guice的Module的配置:

import static com.google.inject.matcher.Matchers.only;
import static com.google.inject.matcher.Matchers.returns;
import static com.google.inject.matcher.Matchers.subclassesOf;

import com.google.inject.AbstractModule;

public class PhoneModule extends AbstractModule {
    protected void configure() {
        bindInterceptor(
            subclassesOf(Phone.class),
            returns(only(Receiver.class)),
            new PhoneLoggerInterceptor(),
            new PhoneRedirectInterceptor()
        );
    }
}

 5.这时,在重新发起打电话的操作(执行第三步创建的类),就可以从console中看到输出的结果了。

 

以上就是Guice的一个Aop的例子,在使用的时候还是比较方便简单的,并且速度上也很快,也很容易理解。而Guice的Aop可以对拦截设定范围,具体的范围可以看一下Api文档,比较简洁的几个操作(但基本上都够用了)。

 

有了这种Aop的操作,就可以在一定程度上对系统的日志(非Log4J这种日志),还有事务,权限等操作进行 Aop编程了。

 

再次感谢Robbie和Robbie所做的工作。

 

Thanks for Bob lee's Guice and Robbie's good book: Apress.Google.Guice.Agile.Lightweight Dependency Injection Framework. All code quoted from this book. Thank you, Robbie Vanbrabant.

你可能感兴趣的:(spring,AOP,编程,log4j,Google)