String AOP 实现原理
Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截。
Spring AOP ⽀持 JDK Proxy 和 CGLIB ⽅式实现动态代理。默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类
spring的切面由包裹222了目标对象的代理类实现,代理类实现处理方法的调用,执行额外的切面逻辑,并调用目标方法。
1.1 织⼊(Weaving):代理的⽣成时机
织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对象中。
在⽬标对象的⽣命周期⾥有多个点可以进⾏织⼊:
编译期:
切面在目标类编程时被织入,需要特殊编译器,AspectJ的织入编译器就是以此方式织入切面。
类加载期:
切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码,AspectJ5的加载时织入,(Load-time weaving.LTW)就支持该方式织入切面。
运行期:
切面在应用运行的某个时刻被织入,一般织入切面时,AOP容器会为目标对象动态创建一个代理对象,SpringAOP就是该方式织入切面。
1.2 动态代理
动态代理通常是在运行时利用 Java 反射机制动态地生成一个代理类,该代理类在运行时织入字节码来完成额外的逻辑,而不是通过修改源代码进行编译期织入。
具体说:动态代理会创建一个实现特定接口的代理对象,在代理对象的方法调用时,会使用反射机制调用原始对象的方法,并在方法调用前后执行其他逻辑。 这个过程中,代理对象的字节码是在运行时生成,并动态织入了额外的字节码。
这种动态代理的实现方式可以帮助我们在运行期间为对象添加新的行为,而不需要修改对象的源代码。这是 AOP(面向切面编程)等技术实现的基础。
1.3 AOP的支持方式
Spring 框架中的AOP,主要基于两种⽅式:JDK 及 CGLIB 的⽅式。这两种⽅式的代理⽬标都是被代理类中的⽅法,在运⾏期,动态的织⼊字节码⽣成代理类。
CGLIB是Java中的动态代理框架,主要作⽤就是根据⽬标类和⽅法,动态⽣成代理类。
Java中的动态代理框架,⼏乎都是依赖字节码框架(如 ASM,Javassist 等)实现的。
字节码框架是直接操作 class 字节码的框架。可以加载已有的class字节码⽂件信息,修改部分信息,或动态⽣成⼀个 class。
JDK 动态代理实现:
JDK 实现时,先通过实现 InvocationHandler 接⼝创建⽅法调⽤处理器,再通过 Proxy 来创建代理类:
//JDK动态代理的实现
//动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被
//代理类必须实现接⼝
public class PayServiceJDKInvocationHandler implements InvocationHandler {
//目标对象就是被代理对象
private Object target;
public PayServiceJDKInvocationHandler(Object target){
this.target=target;
}
//proxy代理对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.安全检查
System.out.println("安全检查");
//2.记录日志
System.out.println("记录日志");
//3.时间统计开始
System.out.println("记录开始时间");
//通过反射调用被代理类的方法
Object retVal=method.invoke(target,args);
//4.时间统计结束
System.out.println("记录结束时间");
return retVal;
}
public static void main(String[] args) {
PayService target= new AliPayService();
//⽅法调⽤处理器
InvocationHandler handler =
new PayServiceJDKInvocationHandler(target);
//创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建
PayService proxy = (PayService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
new Class[]{PayService.class},handler);
proxy.pay();
}
}
CGLIB 动态代理实现
public class PayServiceCGLIBInterceptor implements MethodInterceptor {
//被代理对象
private Object target;
public PayServiceCGLIBInterceptor(Object target){
this.target=target;
}
@Override
public Object intercept(Object o, Method method, Object []args, MethodProxy methodProxy) throws Throwable
{
//1.安全检查
System.out.println("安全检查");
//2.记录日志
System.out.println("记录日志");
//3.时间统计开始
System.out.println("记录开始时间");
//通过cglib的代理方法调用
Object retVal=methodProxy.invoke(target,args);
//4.时间统计结束
System.out.println("记录结束时间");
return retVal;
}
public static void main(String[] args) {
PayService target= new AliPayService();
PayService proxy=(PayService) Enhancer.create(target.getClass(),n
ew PayServiceCGLIBInterceptor(target));
proxy.pay();
}
}
1.4 JDK 和 CGLIB 实现的区别
1. JDK 实现:
要求被代理类必须实现接⼝,之后是通过 InvocationHandler 及 Proxy,在运⾏时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
2. CGLIB 实现:
被代理类可以不实现接⼝,是通过继承被代理类,在运⾏时动态的⽣成代理类对象。
a.出身不同
b.实现不同:
JDK Proxy要求代理类实现接口才能实现代理;CGLIB是通过实现代理类的子类完成动态代理。
c.性能不同:
JDK 7+ JDK Proxy性能略高于CGLIB ;JDK 7之前CGLIB性能原因高于JDK Proxy
总结:
AOP 是对某⽅⾯能⼒的统⼀实现,它是⼀种实现思想,Spring AOP 是对 AOP 的具体实现,SpringAOP 可通过 AspectJ(注解)的⽅式来实现 AOP 的功能,Spring AOP 的实现步骤是:1. 添加 AOP 框架⽀持。2. 定义切⾯和切点。3. 定义通知。Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有两种:JDK Proxy 和 CGLIB