Spring AOP原理(一):从代理模式到动态代理,手写100行代码实现!

系列文章第1篇 | 共3篇
难度:⭐⭐ | 适合人群:想理解AOP底层原理的开发者


💥 开场:一次"灵异"的Bug

时间: 周四下午
地点: 办公室
事件: 代码评审

我: "这是我写的用户服务,加了事务注解..."

@Service
public class UserService {
    
    @Autowired
    private UserDao userDao;
    
    @Transactional
    public void createUser(User user) {
        userDao.save(user);
        System.out.println("用户创建成功");
    }
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            this.createUser(user);  // 调用自己的事务方法
        }
    }
}

哈吉米: "等等,你这个batchCreate有问题。"

我: "啥问题?调用了事务方法啊。" 🤔

哈吉米: "你试试,如果中间某个用户保存失败,前面保存的会回滚吗?"

我: "会啊,有@Transactional呢。"


测试一下:

List<User> users = Arrays.asList(
    new User("张三"),
    new User("李四"),
    new User(""),  // 空名字,会抛异常
    new User("王五")
);

try {
    userService.batchCreate(users);
} catch (Exception e) {
    System.out.println("批量创建失败");
}

// 查询数据库
List<User> all = userDao.findAll();
System.out.println("数据库中有:" + all.size() + " 个用户");

结果:

用户创建成功  // 张三
用户创建成功  // 李四
批量创建失败  // 空名字抛异常

数据库中有:2 个用户  // ← 张三和李四没回滚!

我: "卧槽!事务没生效???" 😱


南北绿豆走过来: "你用this.createUser()调用的,当然不生效。"

我: "为什么?" 😰

阿西噶阿西: "因为@Transactional是通过AOP代理实现的,this调用拿到的是原始对象,不是代理对象,代理逻辑就不会执行!"

我: "代理对象?什么意思?" 🤔

哈吉米: "来,我给你讲讲什么是代理..."


🎯 第一问:什么是代理模式?

生活中的代理

南北绿豆: "你买过房吗?"

我: "租过。"

南北绿豆: "你是直接找房东还是找中介?"

我: "找中介。"

南北绿豆: "这就是代理!"

你 → 中介(代理) → 房东(真实对象)

你不直接接触房东
通过中介来租房
中介可以:
  - 提前检查你的信用
  - 收取中介费
  - 办理合同
  - 后续维护

这就是代理模式!


代理模式定义

官方定义:

为其他对象提供一种代理以控制对这个对象的访问。

人话版本:

真实对象(被代理对象)
    ↓
代理对象(中间人)
    ↓
客户端(调用方)

客户端不直接访问真实对象,
而是通过代理对象访问,
代理可以在调用前后做额外操作。

静态代理实现

场景: 给UserService的方法加日志

接口:

/**
 * 用户服务接口
 */
public interface UserService {
    void createUser(String name);
    void deleteUser(Long id);
}

真实实现:

/**
 * 真实的UserService实现
 */
public class UserServiceImpl implements UserService {
    
    @Override
    public void createUser(String name) {
        System.out.println("创建用户:" + name);
    }
    
    @Override
    public void deleteUser(Long id) {
        System.out.println("删除用户:" + id);
    }
}

代理类:

/**
 * UserService的代理类(静态代理)
 */
public class UserServiceProxy implements UserService {
    
    private UserService target;  // 真实对象
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public void createUser(String name) {
        System.out.println(">>> 【日志】开始创建用户:" + name);
        long start = System.currentTimeMillis();
        
        target.createUser(name);  // 调用真实对象的方法
        
        long end = System.currentTimeMillis();
        System.out.println("<<< 【日志】创建完成,耗时:" + (end - start) + "ms");
    }
    
    @Override
    public void deleteUser(Long id) {
        System.out.println(">>> 【日志】开始删除用户:" + id);
        long start = System.currentTimeMillis();
        
        target.deleteUser(id);  // 调用真实对象的方法
        
        long end = System.currentTimeMillis();
        System.out.println("<<< 【日志】删除完成,耗时:" + (end - start) + "ms");
    }
}

使用:

public class StaticProxyTest {
    public static void main(String[] args) {
        // 创建真实对象
        UserService userService = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = new UserServiceProxy(userService);
        
        // 通过代理调用
        proxy.createUser("张三");
        proxy.deleteUser(1L);
    }
}

输出:

>>> 【日志】开始创建用户:张三
创建用户:张三
<<< 【日志】创建完成,耗时:2ms

>>> 【日志】开始删除用户:1
删除用户:1
<<< 【日志】删除完成,耗时:1ms

成功!


静态代理的缺点

阿西噶阿西: "静态代理有三大缺点!"

缺点1:代码重复

// 如果有10个方法,就要写10遍日志代码
@Override
public void method1() {
    System.out.println(">>> 日志开始");
    target.method1();
    System.out.println("<<< 日志结束");
}

@Override
public void method2() {
    System.out.println(">>> 日志开始");  // 重复
    target.method2();
    System.out.println("<<< 日志结束");  // 重复
}

// ...重复10次

缺点2:类爆炸

// 如果有100个Service,就要写100个代理类
UserServiceProxy
OrderServiceProxy
ProductServiceProxy
PaymentServiceProxy
...
// 100个代理类!

缺点3:不灵活

// 如果接口新增方法,代理类也要改
public interface UserService {
    void createUser(String name);
    void deleteUser(Long id);
    void updateUser(User user);  // ← 新增方法
}

// UserServiceProxy也要新增对应方法
@Override
public void updateUser(User user) {
    // 又要写一遍日志逻辑...
}

哈吉米: "这时候,动态代理就登场了!"


🎨 第二问:JDK动态代理

核心API

南北绿豆: "JDK提供了动态代理的API。"

// 核心类
java.lang.reflect.Proxy

// 核心接口
java.lang.reflect.InvocationHandler

InvocationHandler接口

public interface InvocationHandler {
    
    /**
     * 代理对象的所有方法调用都会进入这里
     * 
     * @param proxy  代理对象
     * @param method 被调用的方法
     * @param args   方法参数
     * @return 方法返回值
     */
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

完整实现

1. 定义InvocationHandler:

package com.example.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 日志代理处理器
 */
public class LogInvocationHandler implements InvocationHandler {
    
    private Object target;  // 真实对象
    
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        // 前置增强
        System.out.println(">>> 【日志】调用方法:" + method.getName());
        System.out.println("    参数:" + java.util.Arrays.toString(args));
        long start = System.currentTimeMillis();
        
        // 调用真实对象的方法
        Object result = method.invoke(target, args);
        
        // 后置增强
        long end = System.currentTimeMillis();
        System.out.println("<<< 【日志】方法返回:" + result);
        System.out.println("    耗时:" + (end - start) + "ms\n");
        
        return result;
    }
}

2. 创建代理对象:

package com.example.proxy;

import java.lang.reflect.Proxy;

public class JdkProxyTest {
    
    public static void main(String[] args) {
        System.out.println("===== JDK动态代理测试 =====\n");
        
        // 1. 创建真实对象
        UserService target = new UserServiceImpl();
        
        // 2. 创建InvocationHandler
        LogInvocationHandler handler = new LogInvocationHandler(target);
        
        // 3. 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 类加载器
            target.getClass().getInterfaces(),   // 接口列表
            handler                               // InvocationHandler
        );
        
        // 4. 通过代理调用
        proxy.createUser("张三");
        proxy.deleteUser(1L);
        
        // 5. 查看代理对象信息
        System.out.println("真实对象类型:" + target.getClass().getName());
        System.out.println("代理对象类型:" + proxy.getClass().getName());
        System.out.println("是否是代理:" + Proxy.isProxyClass(proxy.getClass()));
    }
}

3. 运行结果:

===== JDK动态代理测试 =====

>>> 【日志】调用方法:createUser
    参数:[张三]
创建用户:张三
<<< 【日志】方法返回:null
    耗时:2ms

>>> 【日志】调用方法:deleteUser
    参数:[1]
删除用户:1
<<< 【日志】方法返回:null
    耗时:1ms

真实对象类型:com.example.proxy.UserServiceImpl
代理对象类型:com.sun.proxy.$Proxy0
是否是代理:true

看到了吗?代理对象的类型是$Proxy0,动态生成的!


JDK动态代理原理

阿西噶阿西: "JDK动态代理的原理是在运行时动态生成代理类的字节码。"

生成的代理类(反编译后):

// 这是JDK运行时生成的代理类
public final class $Proxy0 extends Proxy implements UserService {
    
    private static Method m3;  // createUser方法
    private static Method m4;  // deleteUser方法
    
    public $Proxy0(InvocationHandler h) {
        super(h);
    }
    
    // 实现接口方法
    public final void createUser(String name) {
        try {
            // 调用InvocationHandler的invoke方法
            super.h.invoke(this, m3, new Object[]{name});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    public final void deleteUser(Long id) {
        try {
            super.h.invoke(this, m4, new Object[]{id});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    static {
        try {
            m3 = Class.forName("com.example.proxy.UserService")
                .getMethod("createUser", String.class);
            m4 = Class.forName("com.example.proxy.UserService")
                .getMethod("deleteUser", Long.class);
        } catch (Exception e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
}

看懂了吗?

  • 代理类继承Proxy,实现UserService接口
  • 所有方法调用都委托给InvocationHandler
  • 这就是为什么所有方法都进入invoke()方法!

JDK动态代理的限制

哈吉米: "JDK动态代理有一个致命缺点!"

// ✅ 可以:有接口
public interface UserService {
    void createUser(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String name) {
        // ...
    }
}

// 可以创建代理 ✅
UserService proxy = (UserService) Proxy.newProxyInstance(...);
// ❌ 不行:没有接口
public class UserService {
    public void createUser(String name) {
        // ...
    }
}

// 无法创建代理 ❌
// JDK动态代理必须基于接口!

阿西噶阿西: "这时候CGLIB就派上用场了!"


🔧 第三问:CGLIB动态代理

什么是CGLIB?

CGLIB = Code Generation Library(代码生成库)

核心原理: 通过继承目标类,生成子类作为代理

UserService(目标类)
    ↑ 继承
UserService$$EnhancerByCGLIB$$12345678(代理类)

添加依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

完整实现

1. 目标类(无接口):

/**
 * 用户服务(没有接口)
 */
public class UserService {
    
    public void createUser(String name) {
        System.out.println("创建用户:" + name);
    }
    
    public void deleteUser(Long id) {
        System.out.println("删除用户:" + id);
    }
}

2. MethodInterceptor:

package com.example.proxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB方法拦截器
 */
public class LogMethodInterceptor implements MethodInterceptor {
    
    /**
     * 拦截方法调用
     * 
     * @param obj    代理对象
     * @param method 被拦截的方法
     * @param args   方法参数
     * @param proxy  方法代理(用于调用父类方法)
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
            MethodProxy proxy) throws Throwable {
        
        // 前置增强
        System.out.println(">>> 【CGLIB日志】调用方法:" + method.getName());
        System.out.println("    参数:" + java.util.Arrays.toString(args));
        long start = System.currentTimeMillis();
        
        // 调用父类方法(真实对象的方法)
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置增强
        long end = System.currentTimeMillis();
        System.out.println("<<< 【CGLIB日志】方法返回:" + result);
        System.out.println("    耗时:" + (end - start) + "ms\n");
        
        return result;
    }
}

3. 创建代理:

package com.example.proxy;

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    
    public static void main(String[] args) {
        System.out.println("===== CGLIB动态代理测试 =====\n");
        
        // 1. 创建Enhancer
        Enhancer enhancer = new Enhancer();
        
        // 2. 设置父类(目标类)
        enhancer.setSuperclass(UserService.class);
        
        // 3. 设置回调(MethodInterceptor)
        enhancer.setCallback(new LogMethodInterceptor());
        
        // 4. 创建代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 5. 通过代理调用
        proxy.createUser("李四");
        proxy.deleteUser(2L);
        
        // 6. 查看类信息
        System.out.println("代理对象类型:" + proxy.getClass().getName());
        System.out.println("父类类型:" + proxy.getClass().getSuperclass().getName());
    }
}

4. 运行结果:

===== CGLIB动态代理测试 =====

>>> CGLIB日志调用方法:createUser
    参数:[李四]
创建用户:李四
<<< CGLIB日志方法返回:null
    耗时:1ms

>>> CGLIB日志调用方法:deleteUser
    参数:[2]
删除用户:2
<<< CGLIB日志方法返回:null
    耗时:0ms

代理对象类型:com.example.proxy.UserService$$EnhancerByCGLIB$$a1b2c3d4
父类类型:com.example.proxy.UserService

看到了吗?代理类继承了UserService!


CGLIB原理

哈吉米: "CGLIB通过继承生成子类。"

生成的代理类(简化版):

// CGLIB生成的代理类
public class UserService$$EnhancerByCGLIB$$a1b2c3d4 extends UserService {
    
    private MethodInterceptor callback;
    
    public void createUser(String name) {
        // 拦截方法调用
        callback.intercept(this, 
            UserService.class.getMethod("createUser", String.class),
            new Object[]{name},
            this.CGLIB$createUser$0$Proxy);
    }
    
    // CGLIB生成的代理方法
    final void CGLIB$createUser$0(String name) {
        super.createUser(name);  // 调用父类方法
    }
}

CGLIB的限制

// ❌ 无法代理final类
public final class UserService {
    // CGLIB无法继承final类
}

// ❌ 无法代理final方法
public class UserService {
    public final void createUser(String name) {
        // CGLIB无法重写final方法
    }
}

// ❌ 无法代理private方法
public class UserService {
    private void createUser(String name) {
        // CGLIB无法重写private方法
    }
}

// ❌ 无法代理static方法
public class UserService {
    public static void createUser(String name) {
        // CGLIB无法重写static方法
    }
}

📊 第四问:JDK vs CGLIB全方位对比

对比表格

维度JDK动态代理CGLIB动态代理
实现方式实现接口继承目标类
要求必须有接口不需要接口
性能(创建)快 ⚡慢 🐢
性能(调用)稍慢快 ⚡
代理类$Proxy0EnhancerByCGLIBEnhancerByCGLIB
限制必须有接口不能代理final类/方法
依赖JDK自带需要额外依赖
适用场景基于接口编程没有接口的类

性能测试

public class PerformanceTest {
    
    public static void main(String[] args) {
        int count = 100000;
        
        // JDK代理测试
        long jdkCreateTime = testJdkProxyCreate(count);
        long jdkInvokeTime = testJdkProxyInvoke(count);
        
        // CGLIB代理测试
        long cglibCreateTime = testCglibProxyCreate(count);
        long cglibInvokeTime = testCglibProxyInvoke(count);
        
        System.out.println("===== 性能测试结果(" + count + "次)=====");
        System.out.println("JDK代理创建:" + jdkCreateTime + "ms");
        System.out.println("JDK代理调用:" + jdkInvokeTime + "ms");
        System.out.println("CGLIB代理创建:" + cglibCreateTime + "ms");
        System.out.println("CGLIB代理调用:" + cglibInvokeTime + "ms");
    }
    
    // 测试方法实现...
}

测试结果(仅供参考):

===== 性能测试结果(100000次)=====
JDK代理创建:150ms
JDK代理调用:280ms
CGLIB代理创建:450ms
CGLIB代理调用:180ms

结论:

  • JDK创建快,调用稍慢
  • CGLIB创建慢,调用快

🌟 第五问:Spring如何选择代理方式?

默认策略

南北绿豆: "Spring会根据情况自动选择。"

if (目标类有接口) {
    使用JDK动态代理  // 默认
} else {
    使用CGLIB代理
}

强制使用CGLIB

方式1:配置文件

spring:
  aop:
    proxy-target-class: true  # 强制使用CGLIB

方式2:注解

@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
public class AopConfig {
}

验证代理类型

@Service
public class UserService implements IUserService {
    
    @PostConstruct
    public void init() {
        System.out.println("UserService类型:" + this.getClass().getName());
        
        if (AopUtils.isJdkDynamicProxy(this)) {
            System.out.println(">>> 使用的是JDK动态代理");
        } else if (AopUtils.isCglibProxy(this)) {
            System.out.println(">>> 使用的是CGLIB代理");
        }
    }
}

输出(有接口):

UserService类型:com.sun.proxy.$Proxy123
>>> 使用的是JDK动态代理

输出(强制CGLIB):

UserService类型:com.example.UserService$$EnhancerBySpringCGLIB$$a1b2c3d4
>>> 使用的是CGLIB代理

💻 第六问:手写简易版动态代理框架

目标

实现一个简单的代理工具类,支持:

  1. ✅ 自动选择JDK或CGLIB
  2. ✅ 支持方法拦截
  3. ✅ 支持before、after增强

实现代码

package com.example.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 简易版代理工厂
 */
public class SimpleProxyFactory {
    
    /**
     * 创建代理对象
     * 
     * @param target 目标对象
     * @param interceptor 拦截器
     * @return 代理对象
     */
    public static Object createProxy(Object target, MethodInterceptor interceptor) {
        
        Class<?> targetClass = target.getClass();
        
        // 判断是否有接口
        if (targetClass.getInterfaces().length > 0) {
            // 有接口,使用JDK动态代理
            System.out.println(">>> 使用JDK动态代理");
            return createJdkProxy(target, interceptor);
        } else {
            // 无接口,使用CGLIB
            System.out.println(">>> 使用CGLIB代理");
            return createCglibProxy(target, interceptor);
        }
    }
    
    /**
     * 创建JDK代理
     */
    private static Object createJdkProxy(Object target, MethodInterceptor interceptor) {
        
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) 
                        throws Throwable {
                    
                    // 调用拦截器
                    return interceptor.intercept(target, method, args, null);
                }
            }
        );
    }
    
    /**
     * 创建CGLIB代理
     */
    private static Object createCglibProxy(Object target, MethodInterceptor interceptor) {
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new net.sf.cglib.proxy.MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, 
                    MethodProxy proxy) throws Throwable {
                
                // 调用拦截器
                return interceptor.intercept(target, method, args, proxy);
            }
        });
        
        return enhancer.create();
    }
    
    /**
     * 方法拦截器接口
     */
    public interface MethodInterceptor {
        Object intercept(Object target, Method method, Object[] args, Object proxy) 
                throws Throwable;
    }
}

测试

public class SimpleProxyTest {
    
    public static void main(String[] args) {
        System.out.println("===== 简易版代理框架测试 =====\n");
        
        // 测试1:有接口的类(JDK代理)
        System.out.println("--- 测试1:有接口的类 ---");
        IUserService userService = new UserServiceImpl();
        IUserService userProxy = (IUserService) SimpleProxyFactory.createProxy(
            userService,
            (target, method, args1, proxy) -> {
                System.out.println(">>> Before: " + method.getName());
                Object result = method.invoke(target, args1);
                System.out.println("<<< After: " + method.getName() + "\n");
                return result;
            }
        );
        userProxy.createUser("张三");
        
        // 测试2:没接口的类(CGLIB代理)
        System.out.println("\n--- 测试2:没接口的类 ---");
        OrderService orderService = new OrderService();
        OrderService orderProxy = (OrderService) SimpleProxyFactory.createProxy(
            orderService,
            (target, method, args1, proxy) -> {
                System.out.println(">>> Before: " + method.getName());
                Object result = method.invoke(target, args1);
                System.out.println("<<< After: " + method.getName() + "\n");
                return result;
            }
        );
        orderProxy.createOrder(123L);
    }
}

输出:

===== 简易版代理框架测试 =====

--- 测试1:有接口的类 ---
>>> 使用JDK动态代理
>>> Before: createUser
创建用户:张三
<<< After: createUser

--- 测试2:没接口的类 ---
>>> 使用CGLIB代理
>>> Before: createOrder
创建订单:123
<<< After: createOrder

成功!自动选择代理方式!


💡 知识点总结

本篇你学到了什么?

代理模式

  • 为对象提供代理控制访问
  • 可以在调用前后增强功能
  • 静态代理 vs 动态代理

JDK动态代理

  • 基于接口实现
  • Proxy.newProxyInstance()
  • InvocationHandler
  • 运行时生成代理类字节码

CGLIB动态代理

  • 基于继承实现
  • Enhancer
  • MethodInterceptor
  • 生成目标类的子类

JDK vs CGLIB

  • JDK必须有接口,CGLIB不需要
  • JDK创建快,CGLIB调用快
  • CGLIB不能代理final类/方法

Spring的选择策略

  • 默认:有接口用JDK,无接口用CGLIB
  • 可强制使用CGLIB

手写代理框架

  • 自动选择代理方式
  • 支持方法拦截

记忆口诀

代理模式做中介,
JDK基于接口来。
CGLIB继承生子类,
运行时刻动态生。
有接口用JDK代理,
无接口CGLIB来帮忙。
Spring自动会选择,
AOP基于代理搞。

🤔 思考题

问题1: 为什么final方法无法被代理?

问题2: 如果目标类既有接口,又有没在接口中定义的方法,会怎样?

问题3: this调用为什么AOP不生效?

提示: 下一篇会详细解答!


📢 下期预告

《Spring AOP原理(二):@Aspect注解深入,5种通知类型完全掌握!》

下一篇我们将:

  • 深入Spring AOP核心概念
  • 学习@Aspect、@Before、@Around等注解
  • 掌握切点表达式
  • 理解5种通知的执行顺序
  • 实战:自定义日志切面、权限切面

让你从代理进阶到AOP! 🚀


💬 互动时间

你在实际开发中用过动态代理吗?
JDK和CGLIB你更倾向哪个?
还有哪些关于代理的疑问?

欢迎评论区讨论!💭


觉得有帮助?三连支持: 👍 点赞 | ⭐ 收藏 | 🔄 转发

看完这篇,动态代理不再神秘!


下一篇见! 👋