动态AOP
上篇文章我们讲到,AOP分为静态AOP和动态AOP。静态AOP在代码编译之后,已经有代理类或者已经改变了目标对象代码,而动态AOP则是在代码运行过程中才生成的代理类。动态AOP可以用JDK Proxy代理和CGLib的方法来实现。
动态AOP之JDK aop
JDK AOP是基于接口来实现的,即动态生成的代理类是目标对象的接口的子类。下面有一个例子来说明,例子里面实现的是保存一只猫,在aop中输出“我是AOP,在方法执行之后...”。步骤如下:
- 写好接口和实现类(一定要接口)
public interface CatService {
void save();
}
public class CatServiceImpl implements CatService{
@Override
public void save() {
System.out.println("保存了一只猫...");
}
}
- 实现InvocationHandler接口,写好AOP内容
public class LogInvocationHandler implements InvocationHandler {
private Object target;//目标对象
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
//此处为织入的AOP内容,method.invoke表示方法的执行,我们可以在这个方法执行之前或者之后织入我们需要的内容
System.out.println("我是AOP,在方法执行之后...");
return invoke;
}
}
- 创建代理类并执行
public static void main(String[] args) {
//如果想在本地看到生成的代理类,可在此设置
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
CatService catServiceImpl = new CatServiceImpl();
LogInvocationHandler handler = new LogInvocationHandler(catServiceImpl);
//生成代理类
CatService catService = (CatService) Proxy
.newProxyInstance(CatServiceImpl.class.getClassLoader(), catServiceImpl.getClass().getInterfaces(), handler);
catService.save();
}
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
public final class $Proxy0 extends Proxy implements CatService {
...
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void save() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
...
}
如果我们在代码中设置了生成代理类保存到本地,我们可以在com/sun/proxy文件夹中找到我们生成的代理类如上。生成的代理类实现了CatService的接口,继承了Proxy类,在Proxy类中,InvocationHandler h又是它的一个成员变量,所以h.invoke()会调用到LogInvocationHandler的invoke,即执行了我们的aop方法。
JDK动态代理只能针对实现了接口的类生成代理。
动态AOP之CGlib(类代理)
动态AOP还可以通过CGlib来实现,CGlib是通过生成目标对象的子类 + 方法拦截器来实现代理的,所以如果目标对象是final类的话,就不可以用这种方法了。那CGlib是怎么生成目标对象的子类的呢?它用到的是Enhancer的增强功能,我们可以通过Enhancer动态生成一个代码中没有的class对象的实例。先来看看例子:
- pom
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
- Java类
public class CatService {
public void save(){
System.out.println("保存了一只猫...");
}
}
- 实现MethodInterceptor
public class MyProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//利用enhancer动态生成class对象的实例
//设置目标对象为父类
enhancer.setSuperclass(clazz);
//设置回调,在这里指本身,MethodInterceptor 实现了callback接口,如果回调会调用intercept方法
enhancer.setCallback(this);
//使用字节码技术动态创建子类的实例
return enhancer.create();
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
Object result = methodProxy.invokeSuper(target, args);
System.out.println("织入成功...");
return result;
}
}
- 测试
public static void main(String[] args) {
//设置生成的代理类保存到本地
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
MyProxy proxy = new MyProxy();
CatService catService = (CatService) proxy.getProxy(CatService.class);
catService.save();
}
CgLib是指使用字节码技术动态创建子类的实例的一种方法,我们甚至可以找到它创建出来的子类,可以通过参数把生成类保存到本地(比如以上我放在了target/cglib文件夹中),以下就是创建的一个CatService的子类:
public class CatService$$EnhancerByCGLIB$$bcaabe8 extends CatService implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
public final void save() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
var10000.intercept(this, CGLIB$save$0$Method, CGLIB$emptyArgs, CGLIB$save$0$Proxy);
} else {
super.save();
}
}
}
可以看到代理类的成员变量 CGLIB $CALLBACK_0不为空的话,会调用CGLIB$CALLBACK_0的intercept方法,那么CGLIB$CALLBACK_0是什么呢?它是MethodInterceptor 的类型(一个方法拦截器),是我们通过Enhancer setCallback设置进来的MyProxy,我们所需的方法参数传过去即可。
利用cglib创建的动态代理对象的性能,是 JDK 的 10 倍;但是创建动态代理对象所花费的时间上,却比 JDK 多花 8 倍的时间。所以,对于单例模式或者具有实例池的代理类,适合采用 CGLib 技术;反之,则适合采用 JDK 技术。
网友评论