Spring AOP

一、AOP 基础概念

AOP 就是面向切面编程,本质上是对面向对象的补充,毕竟是切面对象,AOP 可以有两种实现方法,一种是动态代理,一种是使用 Spring 来实现,Spring 的话重在会用注解,这里详细讲讲前者,如果是动态代理实现 AOP 的话首先要定义接口,毕竟这样才方便使用动态代理对象执行方法。

二、代理模式

代理模式分为两种,动态代理和静态代理。动态代理是一种在运行时生成代理对象的技术,主要是生成一个增强之后的原始对象,当然还有控制访问的作用。而且动态代理有两种,一种是基于反射的 JDK 动态代理,一种是基于继承的 cglib 动态代理,当然还有静态代理,不过没有动态代理灵活。

三、JDK 动态代理

实现要点

JDK 动态代理的实现有三个关键点,目标接口,目标对象实现目标接口,代理对象实现目标接口,而且代理对象和目标对象是平级关系,关键类有两个,分别是 Proxy 和 InvocationHandler 类,然后就是调用 Proxy 的静态方法 newProxyInstance 创建新的代理实例,接着在 InvocationHandler 中重写 invoke 方法,该方法要用反射实现,也就是 method.invoke(对象,参数),就理解为正常的调用方法就行了。

源码原理

接下来谈谈源码,JDK 动态代理通过 ProxyGenerator 在运行期生成一个实现指定接口的代理类,代理类的方法内部统一委托给 InvocationHandler,从而实现方法增强。通过 Arthas 的 jad 命令 jad com.sun.proxy.$Proxy0 查看代理类,可以反编译得到 .java 源文件,查看动态代理实际生成的代理类。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.demo;

import java.lang.annotation.Target;
import java.lang.reflect.Proxy;

public class TestAOP {
public static void main(String[] args) {
Target proxy = (Target) Proxy.newProxyInstance(
TestAOP.class.getClassLoader(),
new TargetImpl().getClass().getInterfaces(),
(proxy1, method, methodArgs) -> {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(new TargetImpl(), methodArgs);
System.out.println("After method: " + method.getName());
return result;
}
);
}

interface target {
void eat();
}

static class TargetImpl implements target {
@Override
public void eat() {
System.out.println("Eating...");
}
}
}

四、CGLIB 动态代理

实现原理

CGLIB 通过生成目标类的子类并重写方法实现方法增强,底层通过 ASM(一个成熟的字节码操作库) 作为底层字节码生成引擎或字节码生成技术(CGLIB 3.x 版本之前)生成目标类的子类。使用 Enhancer 类时,将要增强的目标类设置为父类,并通过 MethodInterceptor 的 intercept 方法实现增强逻辑。在 intercept 方法中,通常通过 proxy.invokeSuper(obj, args) 调用原方法,前后可以添加切面逻辑。最后,通过 enhancer.create() 创建增强后的代理对象。

注意事项

注意:目标类或方法为 final 时无法被 CGLIB 代理。JDK 动态代理不受 final 方法限制,因为它基于接口;CGLIB 不能增强 final 方法,因为它是通过继承重写实现的。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.demo;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCglibAOP {

public static void main(String[] args) {

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetImpl.class);

enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj,
Method method,
Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
});

TargetImpl proxy = (TargetImpl) enhancer.create();
proxy.eat();
}

static class TargetImpl {
public void eat() {
System.out.println("Eating...");
}
}
}