10、(结构型设计模式)Java 代理模式深度学习指南

以下是为你精心撰写的 《Java 代理模式深度学习指南》,专为 Java 后端开发者打造,内容涵盖:定义、作用、与装饰器模式的深度对比、真实业务场景、JDK 动态代理、CGLIB 代理、Spring AOP 对应、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目实战:权限控制、日志记录、事务管理、远程调用、缓存、限流、监控等核心场景。


📘 Java 代理模式深度学习指南

—— 从“直接调用”到“拦截增强”的架构升华

作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / AOP / RPC / 安全 / 性能监控)
目标:彻底掌握“在不修改原对象的前提下,通过代理对象控制访问”的核心模式,理解 Spring AOP 的底层原理
核心原则“我不是你,但我能代表你;我不是在改你,我是在你前后加了哨兵”


✅ 一、什么是代理模式?

📌 定义:

代理模式(Proxy Pattern) 是一种结构型设计模式,它为其他对象提供一个代理或占位符,以控制对这个对象的访问。代理对象在客户端与真实对象之间起到中介作用

🔍 核心思想:

  • 客户端不直接调用真实对象(Subject)
  • 客户端调用的是代理对象(Proxy)
  • 代理对象持有真实对象的引用
  • 代理对象在调用真实对象前后,可以添加额外逻辑(如权限校验、日志、缓存)
  • 代理对象和真实对象实现相同接口(或继承相同父类)

💡 一句话记忆
“我要找老板谈事,老板太忙,先找秘书——秘书先拦我、记我、查我权限,再决定要不要放我进去见老板。”

🆚 代理模式 vs 装饰器模式(极易混淆)

维度代理模式装饰器模式
目的控制访问(是否能调、何时调、如何调)增强功能(加日志、缓存、加密)
关注点访问控制、延迟加载、安全拦截功能叠加、职责扩展
是否必须接口一致✅ 必须(同接口)✅ 必须(同接口)
是否改变行为可以阻止调用(如权限不足)总是调用原行为 + 增强
典型场景权限校验、远程调用、懒加载日志、缓存、加密、限流
是否透明客户端感知不到代理存在客户端也感知不到装饰器
类比秘书(决定是否放行)给咖啡加奶(增强口味)

关键区别

  • 代理“你能不能见老板?”(控制权)
  • 装饰器“你见老板前先喝杯咖啡?”(增强性)

经典比喻

  • 你去银行取钱:
    • 装饰器:取钱时,自动打印小票(增强)
    • 代理:取钱前,必须刷身份证、人脸识别(控制)

✅ 二、代理模式有什么作用?(Why Use Proxy Pattern?)

作用说明
控制访问权限在调用前校验用户身份、角色、权限
延迟加载(Lazy Loading)对象创建成本高时,先返回代理,真正用时才初始化
远程代理(Remote Proxy)本地对象代表远程服务(如 RPC、Feign)
虚拟代理(Virtual Proxy)占位符,按需加载大对象(如图片、文件)
保护代理(Protection Proxy)控制对敏感对象的访问(如数据库、配置)
智能引用(Smart Reference)在调用前后自动记录日志、统计次数、缓存
事务管理在方法前后自动开启/提交/回滚事务
性能监控自动记录方法执行时间、调用次数
AOP 实现基础Spring AOP 底层就是代理模式的实现

💡 经典名言
A proxy is an object that acts as a stand-in for another object.
——《Design Patterns: Elements of Reusable Object-Oriented Software》


✅ 三、代理模式的典型使用场景(Java 后端真实案例)

场景说明是否推荐使用代理模式
权限控制用户访问接口前校验角色(如:只有管理员能删除)✅ 强烈推荐
事务管理@Transactional 自动开启/提交/回滚事务✅ 强烈推荐
日志记录方法调用前后自动打印参数、耗时、结果✅ 推荐
远程调用Feign、Dubbo、gRPC 的客户端代理✅ 推荐
缓存代理方法调用前查缓存,命中则返回,未命中才调用真实方法✅ 推荐
限流/熔断调用前判断是否超限,超限直接拒绝✅ 推荐
懒加载加载大型对象(如图片、文件、复杂查询)时,先返回代理✅ 推荐
监控/埋点统计接口调用量、成功率、响应时间✅ 推荐
AOP 实现Spring AOP 底层使用代理实现切面✅ 核心基础
简单工具封装StringUtils.isEmpty()❌ 用静态方法
对象创建new Order()❌ 用工厂模式
接口转换微信 SDK → 标准接口❌ 用适配器模式
功能叠加日志 + 缓存 + 加密❌ 用装饰器模式

判断标准
“我需要在调用真实对象之前/之后,插入控制逻辑(如权限、事务、缓存),甚至阻止调用?”
→ 是 → 用代理模式!


✅ 四、代理模式的三种实现方式详解(含中文注释)

我们从静态代理JDK 动态代理,再到CGLIB 代理,最后到Spring AOP


🔹 1. 静态代理(Manual Proxy)—— 手写代理类

适用于固定接口、少量对象,代码量大,不推荐用于生产。

/**
 * 【1】真实对象接口
 */
interface UserService {
    void createUser(String name);
    void deleteUser(Long id);
    String getUserInfo(Long id);
}

/**
 * 【2】真实对象实现
 */
class UserServiceImpl implements UserService {

    @Override
    public void createUser(String name) {
        System.out.println("👤 创建用户:" + name);
    }

    @Override
    public void deleteUser(Long id) {
        System.out.println("🗑️ 删除用户:id=" + id);
    }

    @Override
    public String getUserInfo(Long id) {
        return "用户信息:id=" + id + ", name=张三";
    }
}

/**
 * 【3】静态代理类:实现相同接口,持有真实对象
 */
class UserServiceProxy implements UserService {

    private UserService realService; // 持有真实对象

    public UserServiceProxy(UserService realService) {
        this.realService = realService;
    }

    @Override
    public void createUser(String name) {
        // ✅ 代理增强:权限校验
        if (!hasPermission("CREATE_USER")) {
            System.out.println("🚫 权限不足,禁止创建用户");
            return;
        }
        System.out.println("📝 记录日志:即将创建用户:" + name);
        realService.createUser(name); // 调用真实对象
        System.out.println("✅ 记录日志:用户创建完成");
    }

    @Override
    public void deleteUser(Long id) {
        // ✅ 代理增强:权限校验
        if (!hasPermission("DELETE_USER")) {
            System.out.println("🚫 权限不足,禁止删除用户");
            return;
        }
        System.out.println("📝 记录日志:即将删除用户:id=" + id);
        realService.deleteUser(id);
        System.out.println("✅ 记录日志:用户删除成功");
    }

    @Override
    public String getUserInfo(Long id) {
        // ✅ 代理增强:缓存模拟
        String cacheKey = "user:" + id;
        String cached = getFromCache(cacheKey);
        if (cached != null) {
            System.out.println("⚡ 缓存命中:" + cacheKey);
            return cached;
        }
        System.out.println("🔍 查询数据库:id=" + id);
        String result = realService.getUserInfo(id);
        putToCache(cacheKey, result);
        return result;
    }

    // 模拟权限检查
    private boolean hasPermission(String action) {
        return "admin".equals(System.getProperty("user.role", "guest"));
    }

    // 模拟缓存
    private Map<String, String> cache = new HashMap<>();

    private String getFromCache(String key) {
        return cache.get(key);
    }

    private void putToCache(String key, String value) {
        cache.put(key, value);
    }
}

/**
 * 【4】客户端:调用代理而非真实对象
 */
public class StaticProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        UserService realService = new UserServiceImpl();

        // 创建代理对象,包装真实对象
        UserService proxy = new UserServiceProxy(realService);

        // 设置用户角色为 admin(模拟权限)
        System.setProperty("user.role", "admin");

        // ✅ 客户端完全不知道用了代理
        proxy.createUser("李四"); // ✅ 权限通过,创建成功
        proxy.deleteUser(1L);     // ✅ 权限通过,删除成功
        System.out.println(proxy.getUserInfo(1L)); // ✅ 缓存命中,不查库

        // 模拟普通用户
        System.setProperty("user.role", "guest");
        proxy.createUser("王五"); // ❌ 权限不足,被拦截
    }
}
✅ 优点:
  • 逻辑清晰,容易理解
  • 可完全控制代理行为
❌ 缺点:
  • 每个类都要写一个代理类 → 代码冗余
  • 不支持动态扩展(新增方法必须改代理类)
  • 不适合生产环境

🔹 2. JDK 动态代理(JDK Dynamic Proxy)—— 基于接口的代理(推荐)

利用 Java 反射机制,在运行时动态生成代理类,要求目标对象必须实现接口

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

/**
 * 【1】接口(同上)
 */
interface UserService {
    void createUser(String name);
    void deleteUser(Long id);
    String getUserInfo(Long id);
}

/**
 * 【2】真实对象(同上)
 */
class UserServiceImpl implements UserService {
    @Override
    public void createUser(String name) {
        System.out.println("👤 创建用户:" + name);
    }

    @Override
    public void deleteUser(Long id) {
        System.out.println("🗑️ 删除用户:id=" + id);
    }

    @Override
    public String getUserInfo(Long id) {
        return "用户信息:id=" + id + ", name=张三";
    }
}

/**
 * 【3】动态代理处理器:实现 InvocationHandler
 * 它是代理的核心:所有方法调用都会被转发到这里
 */
class UserServiceInvocationHandler implements InvocationHandler {

    private Object target; // 真实对象

    public UserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // ✅ 在调用真实方法前:权限校验
        String methodName = method.getName();
        if (!hasPermission(methodName)) {
            System.out.println("🚫 权限不足,禁止调用方法:" + methodName);
            return null;
        }

        // ✅ 在调用前:日志记录
        System.out.println("📝 [代理] 方法开始:" + methodName + ",参数:" + java.util.Arrays.toString(args));

        // ✅ 调用真实对象的方法
        Object result = method.invoke(target, args);

        // ✅ 在调用后:日志记录
        System.out.println("✅ [代理] 方法结束:" + methodName + ",返回:" + result);

        return result;
    }

    // 模拟权限检查
    private boolean hasPermission(String methodName) {
        String role = System.getProperty("user.role", "guest");
        return switch (methodName) {
            case "createUser" -> "admin".equals(role);
            case "deleteUser" -> "admin".equals(role);
            case "getUserInfo" -> true; // 所有人可查
            default -> false;
        };
    }
}

/**
 * 【4】客户端:动态生成代理对象
 */
public class JdkDynamicProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        UserService realService = new UserServiceImpl();

        // ✅ 动态生成代理对象
        // Proxy.newProxyInstance():
        //   第一个参数:类加载器
        //   第二个参数:要代理的接口数组
        //   第三个参数:InvocationHandler 实例
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),
                new Class<?>[]{UserService.class},
                new UserServiceInvocationHandler(realService)
        );

        // 设置用户角色
        System.setProperty("user.role", "admin");

        // ✅ 客户端像调用真实对象一样调用代理
        proxy.createUser("李四");   // ✅ 权限通过,执行
        proxy.deleteUser(1L);      // ✅ 权限通过,执行
        System.out.println(proxy.getUserInfo(1L)); // ✅ 缓存逻辑?不,这里只是日志

        // 模拟普通用户
        System.setProperty("user.role", "guest");
        proxy.createUser("王五");   // ❌ 权限不足,被拦截
        System.out.println(proxy.getUserInfo(1L)); // ✅ 仍可查,因为方法允许
    }
}
✅ 优点:
  • 无需手写代理类,运行时动态生成
  • 支持任意接口,只要实现接口即可
  • 符合开闭原则,新增方法无需改代理逻辑
  • Spring AOP 默认使用 JDK 动态代理
⚠️ 缺点:
  • 必须有接口!不能代理没有接口的类
  • 性能略低于 CGLIB(反射调用)

🔹 3. CGLIB 代理(Code Generation Library)—— 基于继承的代理(推荐)

适用于没有接口的类,通过继承目标类 + 重写方法实现代理。

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

import java.lang.reflect.Method;

/**
 * 【1】真实类(无接口)
 */
class OrderService {
    public void createOrder(String productId) {
        System.out.println("🛒 创建订单:商品ID=" + productId);
    }

    public void updateOrder(Long orderId) {
        System.out.println("✏️ 更新订单:id=" + orderId);
    }

    public String getOrderStatus(Long orderId) {
        return "已支付";
    }
}

/**
 * 【2】CGLIB 代理拦截器:实现 MethodInterceptor
 * 它会拦截所有被代理类的方法调用
 */
class OrderServiceInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // ✅ 方法调用前:日志 + 权限
        String methodName = method.getName();
        if (!hasPermission(methodName)) {
            System.out.println("🚫 权限不足,禁止调用:" + methodName);
            return null;
        }

        System.out.println("📝 [CGLIB代理] 方法开始:" + methodName + ",参数:" + java.util.Arrays.toString(args));

        // ✅ 调用原方法(使用 MethodProxy 更高效)
        Object result = proxy.invokeSuper(obj, args);

        // ✅ 方法调用后:日志
        System.out.println("✅ [CGLIB代理] 方法结束:" + methodName + ",返回:" + result);

        return result;
    }

    private boolean hasPermission(String methodName) {
        String role = System.getProperty("user.role", "guest");
        return switch (methodName) {
            case "createOrder", "updateOrder" -> "admin".equals(role);
            case "getOrderStatus" -> true;
            default -> false;
        };
    }
}

/**
 * 【3】客户端:使用 CGLIB 创建代理
 */
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        OrderService realService = new OrderService();

        // ✅ 创建 CGLIB 代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class); // 设置父类
        enhancer.setCallback(new OrderServiceInterceptor()); // 设置拦截器

        OrderService proxy = (OrderService) enhancer.create(); // 生成代理对象

        // 设置权限
        System.setProperty("user.role", "admin");

        // ✅ 客户端像调用真实对象一样调用代理
        proxy.createOrder("P001"); // ✅ 执行
        proxy.updateOrder(1L);     // ✅ 执行
        System.out.println(proxy.getOrderStatus(1L)); // ✅ 执行

        // 普通用户
        System.setProperty("user.role", "guest");
        proxy.createOrder("P002"); // ❌ 被拦截
    }
}
✅ 优点:
  • 不需要接口,可代理任意类
  • 性能优于 JDK 动态代理(CGLIB 使用字节码生成,直接调用,无反射)
  • Spring AOP 在无接口时自动使用 CGLIB
⚠️ 缺点:
  • 不能代理 final 类或 final 方法
  • 依赖第三方库(CGLIB)
  • 生成子类,可能影响序列化、反射

💡 CGLIB 原理
在运行时动态生成目标类的子类,并重写所有非 final 方法,在子类方法中插入拦截逻辑。


🔹 4. 代理模式 + Spring AOP(企业级终极实现)

Spring AOP 是代理模式的框架级封装,通过注解声明切面,底层自动使用 JDK 或 CGLIB 代理。

/**
 * 【1】无接口的真实服务
 */
@Service
public class OrderService {

    public void createOrder(String productId) {
        System.out.println("🛒 创建订单:商品ID=" + productId);
    }

    public void updateOrder(Long orderId) {
        System.out.println("✏️ 更新订单:id=" + orderId);
    }

    public String getOrderStatus(Long orderId) {
        return "已支付";
    }
}

/**
 * 【2】切面:定义增强逻辑
 */
@Aspect
@Component
@Slf4j
public class OrderServiceAspect {

    // 切入点:匹配 OrderService 所有方法
    @Pointcut("execution(* com.example.service.OrderService.*(..))")
    public void orderServiceMethods() {}

    // 前置增强:权限校验 + 日志
    @Before("orderServiceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        log.info("📝 [AOP] 方法开始:{},参数:{}", methodName, args);

        // 模拟权限校验
        String role = System.getProperty("user.role", "guest");
        if (("createOrder".equals(methodName) || "updateOrder".equals(methodName)) && !"admin".equals(role)) {
            throw new SecurityException("🚫 权限不足,禁止操作:" + methodName);
        }
    }

    // 后置增强:记录返回值
    @AfterReturning(pointcut = "orderServiceMethods()", returning = "result")
    public void logAfter(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        log.info("✅ [AOP] 方法结束:{},返回:{}", methodName, result);
    }

    // 异常增强
    @AfterThrowing(pointcut = "orderServiceMethods()", throwing = "ex")
    public void logError(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        log.error("❌ [AOP] 方法 {} 执行失败:{}", methodName, ex.getMessage());
    }
}

/**
 * 【3】配置:启用 AOP
 */
@SpringBootApplication
@EnableAspectJAutoProxy // ✅ 开启 AOP,底层自动选择 JDK 或 CGLIB 代理
public class SpringProxyDemo {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringProxyDemo.class, args);
        OrderService orderService = context.getBean(OrderService.class);

        // 设置用户角色
        System.setProperty("user.role", "admin");

        // ✅ 客户端调用,完全不知道用了代理!
        orderService.createOrder("P001");
        orderService.updateOrder(1L);
        System.out.println(orderService.getOrderStatus(1L));

        // 普通用户
        System.setProperty("user.role", "guest");
        try {
            orderService.createOrder("P002"); // ❌ 抛出 SecurityException
        } catch (SecurityException e) {
            System.out.println(e.getMessage());
        }
    }
}
✅ 优点:
  • 零侵入:业务代码完全不改
  • 声明式:用 @Before@After 等注解声明增强逻辑
  • 自动选择代理:有接口 → JDK 代理;无接口 → CGLIB 代理
  • 集成 Spring:支持事务、缓存、安全等内置切面
  • 企业级标准:Spring Boot 默认使用 AOP 实现所有增强功能
Spring AOP 的本质就是代理模式
代理模式Spring AOP
Proxy 对象@Aspect 切面
InvocationHandler@Before@After 等通知方法
target.invoke()joinPoint.proceed()
手动创建代理@EnableAspectJAutoProxy 自动创建

结论
你每天在用的 @Transactional@Cacheable@Secured@Log,都是代理模式的注解化实现!


✅ 五、代理模式 vs 装饰器模式 vs 适配器模式 对比

模式目的关键词举例
代理模式控制访问(是否能调)“你有权限吗?”权限校验、事务、缓存、Feign
装饰器模式增强功能(加点料)“你喝杯咖啡吗?”日志、加密、限流
适配器模式转换接口(改接口)“你插头不匹配,我给你转接头”微信 SDK → 标准接口

一句话区分

  • 代理“你不能直接见老板,得经过我”
  • 装饰器“你见老板前先喝杯咖啡”
  • 适配器“你用的是 USB-A,老板用的是 USB-C,我给你个转接头”

✅ 六、代理模式的避坑指南(Java 后端高频踩坑)

问题原因解决方案
❌ 用代理模式实现装饰器功能混淆用途✅ 功能增强用装饰器,访问控制用代理
❌ 代理类中写业务逻辑违反单一职责✅ 代理只做拦截、控制,业务逻辑放 Service
❌ 代理方法调用自身方法(this)代理失效✅ 避免在代理类中 this.method(),应注入 @Autowired 的代理对象
❌ 代理 final 方法或类CGLIB 无法代理✅ 避免用 final,或改用接口 + JDK 代理
❌ 没有开启 AOP@Aspect 无效✅ 添加 @EnableAspectJAutoProxy
❌ 事务失效方法是 privatestatic✅ 事务方法必须是 public,且由代理对象调用
❌ 多个代理嵌套顺序错乱优先级混乱✅ 用 @Order 控制切面执行顺序

重要陷阱

@Service
public class UserService {
    public void createUser() {
        updateUser(); // ❌ 这里调用的是 this.updateUser(),不是代理对象!
    }
    
    @Transactional
    public void updateUser() { ... }
}

updateUser() 的事务会失效!因为 this 跳过了代理。

正确做法

@Autowired
private UserService self; // 注入自己(代理对象)

public void createUser() {
    self.updateUser(); // ✅ 走代理,事务生效
}

✅ 七、学习建议与进阶路径

阶段建议
📚 第一周用 JDK 动态代理为 UserService 实现权限校验和日志
📚 第二周用 CGLIB 为无接口的 OrderService 实现限流
📚 第三周用 Spring AOP 实现 @Log 注解,自动记录所有 Controller 方法调用
📚 第四周阅读 Spring 源码:ProxyFactoryDefaultAopProxyFactory 如何选择 JDK/CGLIB?
📚 面试准备准备回答:

“你项目中哪里用了代理模式?”
“JDK 动态代理和 CGLIB 区别?”
“Spring AOP 是代理模式吗?”
“为什么 @Transactional 有时失效?” |


✅ 八、代理模式面试高频题(附答案)

Q1:代理模式解决了什么问题?

A:解决“在不修改原对象的前提下,控制对它的访问”,如权限、事务、缓存、远程调用。

Q2:JDK 动态代理和 CGLIB 代理有什么区别?

A:

  • JDK:基于接口,使用反射,性能稍低,必须有接口
  • CGLIB:基于继承,使用字节码生成,性能高,可代理无接口类,不能代理 final 类
  • Spring 优先用 JDK,无接口时自动用 CGLIB

Q3:Spring AOP 是代理模式吗?

A:是的!Spring AOP 底层就是通过 JDK 动态代理或 CGLIB 代理实现的,@Before@Around 就是代理逻辑。

Q4:为什么 @Transactional 有时不生效?

A:

  1. 方法不是 public
  2. 方法是 privatestatic
  3. 在类内部 this.method() 调用,绕过了代理
  4. 没有启用 AOP:缺少 @EnableAspectJAutoProxy

Q5:代理模式和装饰器模式的区别?

A:

  • 代理:控制访问(是否能调)
  • 装饰器:增强功能(怎么调)
    → 一个管“能不能”,一个管“加不加料”

✅ 九、总结:代理模式选型决策树

graph TD
    A[需要控制对对象的访问吗?] --> B{目标对象有接口?}
    B -->|是| C[使用 JDK 动态代理]
    B -->|否| D[使用 CGLIB 代理]
    C --> E[是否在 Spring 项目中?]
    D --> E
    E -->|是| F[用 @Aspect + @Before/@Around 实现 AOP]
    E -->|否| G[手动使用 Proxy.newProxyInstance 或 Enhancer]

最终推荐

  • Spring 项目 → ✅ AOP + @Aspect(最优雅、最标准)
  • 非 Spring、有接口 → ✅ JDK 动态代理
  • 非 Spring、无接口 → ✅ CGLIB 代理
  • 绝对不要:手写静态代理(除非教学)、在代理中写业务逻辑、忽略 @EnableAspectJAutoProxy

✅ 十、结语:真正的高手,不改代码,只设门卫

你不是在学“代理模式”,你是在学“如何在不破坏系统稳定性的前提下,实现安全、监控、事务等横切关注点”。

当你看到 @Transactional 自动帮你开启事务、提交、回滚,而业务代码一行没改时,你已经掌握了 Spring 的灵魂。
当你用 @Aspect 统一记录所有接口的访问日志、性能监控、异常追踪时,你已经是架构师了。

不要为了“用模式”而用模式,
要为了“系统可安全、可监控、可维护”而选择它。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值