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..."); } } }
|