JAVA: Google Guice 学习AOP (面向切面的编程)

Guice的AOP还是很弱的,目前仅仅支持方法级别上的,另外灵活性也不是很高。看如下示例:

Guice支持AOP的条件是:

类必须是public或者package (default)
类不能是final类型的
方法必须是public,package或者protected
方法不能使final类型的
实例必须通过Guice的@Inject注入或者有一个无参数的构造函数

且看示例代码

1、定义接口

package com.guice.AOP;

import com.google.inject.ImplementedBy;

@ImplementedBy(ServiceImpl.class)
public interface Service {
    public void sayHello();
}

2、定义实现类

package com.guice.AOP;

import com.google.inject.Singleton;
import com.google.inject.name.Named;

@Singleton
public class ServiceImpl implements Service {

    @Named("log")
    @Override
    public void sayHello() {
        System.out.println(String.format("[%s#%d] execute %s at %d", this.getClass().getSimpleName(), hashCode(), "sayHello", System.nanoTime()));
    }
}

3、定义切面

package com.guice.AOP;

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

/**
 *  TODO :自定义的方法拦截器,用于输出方法的执行时间
 * 
 * @author E468380
 */
public class LoggerMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String name = invocation.getMethod().getName();
        long startTime = System.nanoTime();
        System.out.println(String.format("before method[%s] at %s", name, startTime));

        Object obj = null;
        try {
            obj = invocation.proceed();// 执行服务
        } finally {
            long endTime = System.nanoTime();
            System.out.println(String.format("after method[%s] at %s, cost(ns):%d", name, endTime, (endTime - startTime)));
        }
        return obj;
    }
}

4、测试类

package com.guice.AOP;

import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Names;

public class AopTest {
    @Inject
    private Service service;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {

            @Override
            public void configure(Binder binder) {
                binder.bindInterceptor(Matchers.any(), Matchers.annotatedWith(Names.named("log")), new LoggerMethodInterceptor());
            }
        });
        injector.getInstance(AopTest.class).service.sayHello();
        injector.getInstance(AopTest.class).service.sayHello();
        injector.getInstance(AopTest.class).service.sayHello();
    }
}

输出结果:

 before method[sayHello] at 18832801981960
 [ServiceImpl$$EnhancerByGuice$$d4244950#1109685565] execute sayHello at 18832817170768
 after method[sayHello] at 18832817378285, cost(ns):15396325
 before method[sayHello] at 18832817542181
 [ServiceImpl$$EnhancerByGuice$$d4244950#1109685565] execute sayHello at 18832817640327
 after method[sayHello] at 18832817781772, cost(ns):239591
 before method[sayHello] at 18832817920651
 [ServiceImpl$$EnhancerByGuice$$d4244950#1109685565] execute sayHello at 18832818013023
 after method[sayHello] at 18832818132657, cost(ns):212006

关于此结果有几点说明:

(1)由于使用了AOP我们的服务得到的不再是我们写的服务实现类了,而是一个继承的子类,这个子类应该是在内存中完成的。

(2)除了第一次调用比较耗时外(可能guice内部做了比较多的处理),其它调用事件为0毫秒(我们的服务本身也没做什么事)。

(3)确实完成了我们期待的AOP功能。

 

5、切面注入依赖

如果一个切面(拦截器)也需要注入一些依赖怎么办?

在这里我们声明一个前置服务,输出所有调用的方法名称。

①定义接口

package com.guice.AOP;

import org.aopalliance.intercept.MethodInvocation;

import com.google.inject.ImplementedBy;

@ImplementedBy(BeforeServiceImpl.class)
public interface BeforeService {
    void before(MethodInvocation invocation);
}

②定义实现类

package com.guice.AOP;

import org.aopalliance.intercept.MethodInvocation;

public class BeforeServiceImpl implements BeforeService {
    @Override
    public void before(MethodInvocation invocation) {
        System.out.println("Before--->" + invocation.getClass().getName());
    }
}

③定义切面

package com.guice.AOP;

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

import com.google.inject.Inject;

//这个切面依赖前置服务
public class AfterMethodIntercepter implements MethodInterceptor {
    @Inject
    private BeforeService beforeService;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        beforeService.before(invocation);
        Object obj = null;
        try {
            obj = invocation.proceed();
        } finally {
            System.out.println("after--->" + invocation.getClass().getName());
        }
        return obj;
    }
}

④编写测试类

package com.guice.AOP;

import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Names;

public class AopTest2 {
    @Inject
    private Service service;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {

            @Override
            public void configure(Binder binder) {
                AfterMethodIntercepter after = new AfterMethodIntercepter();
                binder.requestInjection(after);
                binder.bindInterceptor(Matchers.any(), Matchers.annotatedWith(Names.named("log")), after);
            }
        });
        injector.getInstance(AopTest2.class).service.sayHello();
    }

}

输出结果:

 Before--->com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation
 [ServiceImpl$$EnhancerByGuice$$618294e9#506575947] execute sayHello at 20140444543338
 after--->com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation

说明
Binder绑定一个切面的API是:

com.google.inject.Binder.bindInterceptor(Matcher<? super Class<?>>, Matcher<? super Method>, MethodInterceptor...)

第一个参数是匹配类,第二个参数是匹配方法,第三个数组参数是方法拦截器。也就是说目前为止Guice只能拦截到方法,然后才做一些切面工作。

注意

尽管切面允许注入其依赖,但是这里需要注意的是,如果切面依赖仍然走切面的话那么程序就陷入了死循环,很久就会堆溢出。
Guice的AOP还是很弱的,目前仅仅支持方法级别上的,另外灵活性也不是很高。

本文: JAVA: Google Guice 学习AOP (面向切面的编程)

Leave a Reply