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还是非常弱的,眼下只支持方法级别上的。另外灵活性也不是非常高。

原文地址:https://www.cnblogs.com/yfceshi/p/7147739.html