Spring AOP 原理——代理模式

目录

一、代理模式

1.1  静态代理

1.2 动态代理

1.2.1 JDK动态代理

1.2.2 CGLIB动态代理


Spring AOP 是基于动态代理来实现AOP的。

一、代理模式

代理模式, 也叫委托模式。该模式是为其他对象提供⼀种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。

在某些情况下, 一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

使用代理前:

使用代理后:

代理模式的主要角色:

  1. Subject: 业务接口类。可以是抽象类或者接口(不一定有)。
  2. RealSubject: 业务实现类。具体的业务执行, 也就是被代理对象。
  3. 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,常见的实现方式有两种:
  1. JDK动态代理
  2. CGLIB动态代理

1.2.1 JDK动态代理

JDK 动态代理类实现步骤:
  1. 定义一个接口及其实现类(静态代理中的 HouseSubject RealHouseSubject );
  2. 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 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 动态代理类实现步骤:
  1. 定义⼀个类(被代理类);
  2. 定义 MethodInterceptor 并重写 intercept 方法, intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 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。
  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值