目录
一、代理模式
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{
@Override
public void rentHouse() {
System.out.println("我是房东, 我出租房⼦");
}
}
//3.代理(中介, 帮房东出租房⼦)
public class HouseProxy implements HouseSubject{
//将被代理对象声明为成员变量
private HouseSubject houseSubject;
public HouseProxy(HouseSubject houseSubject) {
this.houseSubject = houseSubject;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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。