Guice 学习(八)AOP (面向切面的编程)

时间:2023-12-05 09:54:20

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