跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava

Spring AOP 原理:代理模式与源码解析

综述由AI生成Spring AOP 基于动态代理的实现原理。首先介绍了代理模式的定义、角色及静态代理的局限性,随后对比了 JDK 动态代理(基于接口)和 CGLIB 动态代理(基于继承)的区别与实现步骤。最后结合 Spring Boot 配置分析了 proxyTargetClass 对代理方式的影响,并通过未实现接口和实现了接口两种场景验证了 JDK 与 CGLIB 的选择逻辑。

CoderByte发布于 2026/3/24更新于 2026/5/1223 浏览
Spring AOP 原理:代理模式与源码解析

Spring AOP 原理:代理模式与源码解析

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

代理模式

代理模式,也叫委托模式。

定义

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

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

使用代理前:

文章配图

使用代理后:

文章配图

代理模式的主要角色

  1. Subject: 业务接口类。可以是抽象类或者接口(不一定有)
  2. RealSubject: 业务实现类。具体的业务执行,也就是被代理对象。
  3. Proxy: 代理类。RealSubject 的代理。

UML 类图如下:

文章配图

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。 根据代理的创建时期,代理模式分为静态代理和动态代理。

• 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class 文件就已经存在了。 • 动态代理:在程序运行时,运用反射机制动态创建而成。

静态代理

静态代理:在程序运行前,代理类的 .class 文件就已经存在了。

(以房租租赁为例,在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了)

下面我们通过代码来加深理解:

  1. 定义接口 (定义房东要做的事情,也是中介需要做的事情)
public interface HouseSubject { 
    void rentHouse(); 
}
  1. 实现接口 (房东出租房子)
public class RealHouseSubject implements HouseSubject {
    @Override 
    public void rentHouse() { 
        System.out.println("我是房东,我出租房子"); 
    } 
}
  1. 代理 (中介,帮房东出租房子)
public class HouseProxy implements HouseSubject {
    private HouseSubject target;
    
    public HouseProxy(HouseSubject target) { 
        this.target = target; 
    }
    
    @Override 
    public void rentHouse() { 
        //出租前
        System.out.println("我是中介,开始代理"); 
        //出租房子
        target.rentHouse(); 
        //出租后
        System.out.println("我是中介,结束代理"); 
    } 
}
  1. 使用
public class Main { 
    public static void main(String[] args) { 
        //静态代理
        HouseProxy proxy = new HouseProxy(new RealHouseSubject()); 
        proxy.rentHouse(); 
    } 
}

运行结果:

文章配图

上面这个代理实现方式就是静态代理。 从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活。所以日常开发几乎看不到静态代理的场景。

接下来新增需求:中介又新增了其他业务:代理房屋出售,此时我们需要对上述代码进行修改:

  1. 接口定义修改
public interface HouseSubject { 
    void rentHouse(); 
    void saleHouse(); 
}
  1. 接口实现修改
public class RealHouseSubject implements HouseSubject {
    @Override 
    public void rentHouse() { 
        System.out.println("我是房东,我出租房子"); 
    } 
    @Override 
    public void saleHouse() { 
        System.out.println("我是房东,我出售房子"); 
    } 
}
  1. 代理类修改
public class HouseProxy implements HouseSubject {
    private HouseSubject target;
    
    public HouseProxy(HouseSubject target) { 
        this.target = target; 
    }
    
    @Override 
    public void rentHouse() { 
        //出租前
        System.out.println("我是中介,开始代理"); 
        //出租房子
        target.rentHouse(); 
        //出租后
        System.out.println("我是中介,结束代理"); 
    }
    
    @Override 
    public void saleHouse() { 
        //出租前
        System.out.println("我是中介,开始代理"); 
        //出租房子
        target.saleHouse(); 
        //出租后
        System.out.println("我是中介,结束代理"); 
    } 
}

从上述代码可以看出,我们修改接口和业务实现类时,还需要修改代理类。 同样的,如果有新增接口和业务实现类,也需要对每一个业务实现类新增代理类。 既然代理的流程是一样的,有没有一种办法,让他们通过一个代理类来实现呢? 这就需要用到动态代理技术了。

动态代理

相比于静态代理来说,动态代理更加灵活。 我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由 JVM 来实现。也就是说动态代理在程序运行时,根据需要动态创建生成。

Java 也对动态代理进行了实现,并给我们提供了一些 API,常见的实现方式有两种:

  1. JDK 动态代理
  2. CGLIB 动态代理
JDK 动态代理

JDK 动态代理类实现步骤:

  1. 定义一个接口及其实现类 (静态代理中的 HouseSubject 和 RealHouseSubject )
  2. 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法 (被代理类的方法) 并自定义一些处理逻辑
  3. 通过 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法创建代理对象
  1. 定义一个接口
public interface HouseSubject { 
    void rentHouse(); 
    void saleHouse(); 
}
  1. 定义一个实现类
public class RealHouseSubject implements HouseSubject {
    @Override 
    public void rentHouse() { 
        System.out.println("我是房东,我出租房子"); 
    } 
    @Override 
    public void saleHouse() { 
        System.out.println("我是房东,我出售房子"); 
    } 
}
  1. 实现 InvocationHandler 接口并重写 invoke 方法
import 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 result = method.invoke(target, args); 
        System.out.println("我是代理,结束代理"); 
        return result; 
    } 
}
  1. 创建一个代理对象并使用
import java.lang.reflect.Proxy;

public class Main { 
    public static void main(String[] args) { 
        //JDK 动态代理
        //目标对象
        RealHouseSubject target = new RealHouseSubject(); 
        //动态生成代理对象 代理接口,运行成功
        HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
            HouseSubject.class.getClassLoader(), 
            new Class[]{HouseSubject.class}, 
            new JDKInvocationHandler(target)); 
        proxy.rentHouse(); 
        proxy.saleHouse(); 
    } 
}

运行结果:

文章配图

接口介绍

InvocationHandler

InvocationHandler 接口是 Java 动态代理的关键接口之一,它定义了一个单一方法 invoke() ,用于处理被代理对象的方法调用。

public interface InvocationHandler {
    /**
     * 参数说明
     * proxy:代理对象
     * method:代理对象需要实现的方法,即其中需要重写的方法
     * args:method 所对应方法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。

Proxy

Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

文章配图

这个方法一共有 3 个参数:

Loader: 类加载器,用于加载代理对象。 interfaces : 被代理类实现的一些接口 (这个参数的定义,也决定了 JDK 动态代理只能代理实现了一些接口的类) h : 实现了 InvocationHandler 接口的对象

如果让 JDK 代理类的话

import java.lang.reflect.Proxy;

public class Main { 
    public static void main(String[] args) { 
        //JDK 动态代理
        //目标对象
        RealHouseSubject target = new RealHouseSubject(); 
        //JDK 代理类
        RealHouseSubject proxy = (RealHouseSubject) Proxy.newProxyInstance(
            RealHouseSubject.class.getClassLoader(), 
            new Class[]{RealHouseSubject.class}, 
            new JDKInvocationHandler(target)); 
        proxy.rentHouse(); 
        proxy.saleHouse(); 
    } 
}

运行结果:

文章配图

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() 创建代理类

代码示例

HouseSubject

public interface HouseSubject { 
    void rentHouse(); 
    void saleHouse(); 
}

RealHouseSubject

public class RealHouseSubject implements HouseSubject {
    @Override 
    public void rentHouse() { 
        System.out.println("我是房东,我出租房子"); 
    } 
    @Override 
    public void saleHouse() { 
        System.out.println("我是房东,我出售房子"); 
    } 
}

CGLibMethodInterceptor

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CGLibMethodInterceptor implements MethodInterceptor {
    private Object target;
    
    public CGLibMethodInterceptor(Object target) { 
        this.target = target; 
    }
    
    /**
     * 调用代理对象的方法
     */
    @Override 
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
        System.out.println("我是中介,开始代理"); 
        Object result = method.invoke(target, args); 
        System.out.println("我是中介,结束代理"); 
        return result; 
    } 
}

Main

import org.springframework.cglib.proxy.Enhancer;

public class Main { 
    public static void main(String[] args) { 
        // 使用 CGLib 完成代理
        //目标对象
        HouseSubject target = new RealHouseSubject(); 
        //代理接口
        HouseSubject houseSubject = (HouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target)); 
        houseSubject.rentHouse(); 
        houseSubject.saleHouse(); 
    } 
}

运行结果:

文章配图

Main

import org.springframework.cglib.proxy.Enhancer;

public class Main { 
    public static void main(String[] args) { 
        // 使用 CGLib 完成代理
        //目标对象
        HouseSubject target = new RealHouseSubject(); 
        //代理类
        RealHouseSubject houseSubject = (RealHouseSubject) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target)); 
        houseSubject.rentHouse(); 
        houseSubject.saleHouse(); 
    } 
}

运行结果:

文章配图

接口介绍

MethodInterceptor

文章配图

参数说明:

o: 被代理的对象 method: 目标方法 (被拦截的方法,也就是需要增强的方法) objects: 方法入参 methodProxy: 用于调用原始方法

Enhancer.create()

Enhancer.create() 用来生成一个代理对象。

文章配图

参数说明:

argumentTypes:被代理类的类型(类或接口) arguments:自定义方法拦截器 MethodInterceptor

Spring AOP 源码解析

Spring AOP 主要基于两种方式实现的:JDK 及 CGLIB 的方式。

Spring 对于 AOP 的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成。 生成代理对象的逻辑在父类 AbstractAutoProxyCreator 中。

文章配图

在上面的代码中有一个重要的属性:proxyTargetClass,默认值为 false。也可以通过程序设置。

proxyTargetClass目标对象代理方式
false实现了接口JDK 代理
false未实现接口(只有实现类)CGLIB 代理
true实现了接口CGLIB 代理
true未实现接口(只有实现类)CGLIB 代理

注意

Spring Boot 2.X 开始,默认使用 CGLIB 代理 可以通过配置项 spring.aop.proxy-target-class=false 来进行修改,设置默认为 jdk 代理, 原来可以使用 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置,但是 SpringBoot 设置 @EnableAspectJAutoProxy 无效,因为 Spring Boot 默认使用 AopAutoConfiguration 进行装配

验证

没实现接口

TestController

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @MyAspect
    @RequestMapping("/t1")
    public String t1() {
        log.info("执行 t1 方法...");
        return "t1";
    }
}

SpringAopApplication

import com.wmh.springaop.controller.TestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringAopApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
        TestController bean = context.getBean(TestController.class);
        System.out.println(bean);
    }
}

设置配置项 spring.aop.proxy-target-class=false 时 Debug 模式下查看到的:

文章配图

设置配置项 spring.aop.proxy-target-class=true 时 Debug 模式下查看到的:

文章配图

实现了接口

Iface

public interface Iface { 
    void test(); 
}

MyAspect

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect { }

TestController2

import com.wmh.springaop.config.MyAspect;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController2 implements Iface {
    @MyAspect
    @RequestMapping("/test1")
    public void test() {
        System.out.println("测试");
    }
}

SpringAopApplication

import com.wmh.springaop.controller.Iface;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringAopApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);
        Iface iface = (Iface) context.getBean("testController2");
        System.out.println(iface);
    }
}

设置配置项 spring.aop.proxy-target-class=false 时 Debug 模式下查看到的:

文章配图

设置配置项 spring.aop.proxy-target-class=true 时 Debug 模式下查看到的:

文章配图

小结

Spring 默认 proxyTargetClass 为 false,实现了接口,使用 JDK 代理,未实现接口,使用 CGLIB 代理 SpringBoot 默认 proxyTargetClass 为 true,默认使用 CGLIB 代理 但是可以通过设置配置项 spring.aop.proxy-target-class 指定 proxyTargetClass。

目录

  1. Spring AOP 原理:代理模式与源码解析
  2. 代理模式
  3. 定义
  4. 代理模式的主要角色
  5. 静态代理
  6. 动态代理
  7. JDK 动态代理
  8. 接口介绍
  9. CGLIB 动态代理
  10. Spring AOP 源码解析
  11. 验证
  12. 没实现接口
  13. 实现了接口
  14. 小结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Linux 文件系统架构、原理与实战操作指南
  • 基于 Sora 等 AI 视频大模型的 48 种商业化应用路径
  • 网络安全工程师职业发展路径与行业现状分析
  • GitHub 项目上传实战指南
  • Stable Diffusion 本地部署与使用指南
  • FAST_LIO 与 FAST_LIO2 算法原理及环境复现
  • AI 前端提示词设计:原理与最佳实践
  • 构建生产环境中的大型语言模型(LLMs)
  • C++ ODB ORM 框架使用指南
  • C++ STL 容器适配器详解:Stack、Queue 与 Priority Queue 的本质与实现
  • AI-Goofish-Monitor:基于 Playwright 和 AI 的闲鱼商品监控工具
  • C++ STL 标准模板库复习:算法与容器详解
  • Git 推送报错“密码认证不支持”?切换到 SSH 密钥方案
  • OpenClaw 飞书机器人权限配置与安全指南
  • 基于微信小程序与 SpringBoot 的新农改管理系统设计与实现
  • Windows 系统下 Stable Diffusion WebUI 环境搭建指南
  • C++ 精学笔记:基础类型与语法实践
  • 前端安全实践:密码加密与 XSS CSRF 防护
  • Android 性能优化实战指南:核心原理与面试应对策略
  • Python 与 Java:AI 项目技术选型对比

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online