Spring AOP 原理——代理模式
目录
一、代理模式
1.1 静态代理
1.2 动态代理
1.2.1 JDK动态代理
1.2.2 CGLIB动态代理
Spring AOP 是基于动态代理来实现AOP的。
一、代理模式
代理模式, 也叫委托模式。该模式是为其他对象提供⼀种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。
在某些情况下, 一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
使用代理前:
使用代理后:
代理模式的主要角色:
- Subject: 业务接口类。可以是抽象类或者接口(不一定有)。
- RealSubject: 业务实现类。具体的业务执行, 也就是被代理对象。
- Proxy: 代理类。RealSubject的代理。
根据代理的创建时期,代理模式分为静态代理和动态代理:
- 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
- 动态代理:在程序运行时,运用反射机制动态创建而成。
1.1 静态代理
静态代理:在程序运行前, 代理类的 .class文件就已经存在了。
举例:房租租赁
//1.定义接口(中介需要做的事情)
public interface HouseSubject {void rentHouse();
}//2. 实现接⼝(房东出租房⼦)
public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东, 我出租房⼦");}
}//3.代理(中介, 帮房东出租房⼦)
public class HouseProxy implements HouseSubject{//将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;}@Overridepublic void rentHouse() {//开始代理System.out.println("我是中介, 开始代理");//代理房东出租房⼦houseSubject.rentHouse();//代理结束System.out.println("我是中介, 代理结束");}
}//4.使用
public class StaticMain {public static void main(String[] args) {HouseSubject subject = new RealHouseSubject();//创建代理类HouseProxy proxy = new HouseProxy(subject);//通过代理类访问⽬标⽅法proxy.rentHouse();}
}
这种方式将代码写死,非常不灵活,如果后续需要增加业务,只能一个个增加修改。
1.2 动态代理
相比于静态代理来说,动态代理更加灵活。该代理不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现。也就是说动态代理在程序运行时,根据需要动态创建生成。
Java也对动态代理进行了实现,并给我们提供了一些API,常见的实现方式有两种:
- JDK动态代理
- CGLIB动态代理
1.2.1 JDK动态代理
JDK 动态代理类实现步骤:
- 定义一个接口及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject );
- 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑;
- 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[ ] interfaces,InvocationHandler h) 方法创建代理对象。
简单实现:
//1.实现 InvocationHandler 接口
mport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {//⽬标对象即就是被代理对象private Object target;public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {// 代理增强内容System.out.println("我是中介, 开始代理");//通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);//代理增强内容System.out.println("我是中介, 代理结束");return retVal;}
}//2.创建⼀个代理对象并使用
public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();//创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}
}
其中:
- InvocationHandler接口是Java动态代理的关键接口之一,它定义了一个单一方法 invoke() ,用于处理被代理对象的方法调用。
public interface InvocationHandler {/*** 参数说明* proxy:代理对象* method:代理对象需要实现的⽅法,即其中需要重写的⽅法* args:method所对应⽅法的参数*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。
- Proxy 类中使用频率最高的方法是: newProxyInstance() , 这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{//...代码省略}
这个方法一共有 3 个参数:
- Loader:类加载器, 用于加载代理对象;
- interfaces:被代理类实现的一些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的一些类);
- h :实现了 InvocationHandler 接口的对象。
1.2.2 CGLIB动态代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。有些场景下, 业务代码是直接实现的,并没有接口定义。此时 CGLIB 动态代理机制可以解决这个问题。
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了CGLIB。例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理, 否则采用 CGLIB 动态代理。
CGLIB 动态代理类实现步骤:
- 定义⼀个类(被代理类);
- 自定义 MethodInterceptor 并重写 intercept 方法, intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似;
- 通过 Enhancer 类的 create( )创建代理类。
简单实现:
1.添加依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
2. 自定义MethodInterceptor(方法拦截器)
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;//实现MethodInterceptor接口
public class CGLIBInterceptor implements MethodInterceptor {//⽬标对象, 即被代理对象private Object target;public CGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {// 代理增强内容System.out.println("我是中介, 开始代理");//通过反射调⽤被代理类的⽅法Object retVal = methodProxy.invoke(target, objects);//代理增强内容System.out.println("我是中介, 代理结束");return retVal;}
}
3.创建代理类, 并使用
public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();HouseSubject proxy= (HouseSubject)
Enhancer.create(target.getClass(),new CGLIBInterceptor(target));proxy.rentHouse();}
}
其中:
- MethodInterceptor 和 JDK动态代理中的 InvocationHandler 类似,它只定义了一个方法 intercept(),用于增强目标方法。
/*** 参数说明:* o: 被代理的对象* method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法)* objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法*/Object intercept(Object o, Method method, Object[] objects, MethodProxy
methodProxy) throws Throwable;
}
- Enhancer.create() 用来生成一个代理对象
public static Object create(Class type, Callback callback) {//...代码省略
}
这个方法中的参数说明:
- type:被代理类的类型(类或接口);
- callback:自定义方法拦截器 MethodInterceptor。