代理模式——举例说明Java23种设计模式中的代理模式

1. 概述

1.1 定义

  • 代理模式(Proxy),顾名思义,有代表打理的意思。在某种情况下,当客户端不能或不适合直接访问目标业务对象时,目标业务对象可以通过代理把自己的业务托管起来,使客户端间接地通过代理进行业务访问。意思就是,代理方以目标业务对象的名义代理了它的业务。
  • 就比如生活中常见的例子:明星经纪人对明星推广业务的代理,家用路由器代理互联网服务上网业务等等。

1.2 组成部分

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

2. 静态代理

2.1 引出代理模式的一个简单业务场景

  • 看下面简单的例子,现在要对实现类里的 playBall() 方法增强功能,比方说在狗狗玩球之前先要有球(即买球,买多大的球等),需要在下面业务功能的基础上怎么扩展?
package com.liu.susu.design.pattern.proxy.statics.example1;

/**
 * @Description 使用静态代理前思考业务场景:
 *              业务需求:现在在狗狗拿到球之前需要添加新的功能,
 *                      比方说根据狗狗的年龄等判断买大小合适的球,
 *                      这种情况我们怎么扩展现在的逻辑,即怎么实现对 playBall() 方法增强功能?
 * @Author susu
 * @date 2022-02-11
 **/
public interface PetPlayInterface {
    void playBall();//玩球
}

class DogPlay implements PetPlayInterface {

    @Override
    public void playBall() {
        System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
    }
}
  • 根据上面的代码,如果要进行扩展,可以直接在原有的 playBall() 方法里添加业务逻辑,但是一般我们扩展功能尽量保持在原有方法不动的基础上进行扩展,要怎么实现,就是我们接下来的代理模式,请先看下面的用静态代理实现。

2.2 静态代理例子1:

  • 直接看代码:
    ① 实现业务的接口 和 实现业务的被代理类
    在这里插入图片描述
    ② 代理类以及增强被代理的方法
    在这里插入图片描述
    ③ 测试类
    在这里插入图片描述
    我们看到,代理类再调方法时,虽然没用new 被代理类,但是被代理类方法执行了(在代理类里),这就是静态代理模式,我们再举个例子,你就会更明白些

2.3 静态代理例子2:

  • 上面例子是代理买球,接下来这个例子稍微复杂一点点,根据狗狗年龄来判断买大球还是小球(比如年龄大于1岁,买大号球,否则小号球)
  • 直接看代码来感受
    ① 在上述的基础上,公共接口和被代理类保持不变
    在这里插入图片描述
    ② 增加一个实体类 Dogs
    在这里插入图片描述
    ③ 代理类
    在这里插入图片描述
    ④ 测试类
    在这里插入图片描述

3. 动态代理

3.1 JDK代理(接口代理)

  • 就是代理类在实例化是是动态生成的,也就是说我们不需要专门针对某个接口去编写代码获取一个代理类,而是在接口运行时动态生成。对,理论很好说简单一句话,还是看看代码怎么实现的吧!

3.1.1 JDK动态代理例子1

  • 为了便于对比,接下来的例子都是两个
  • 我们来根据代码说明:
    ① PetPlayInterface、DogPlay、PetPlayProxy、Dogs同我们静态代理2的代码,此处不再截图了;
    ② 新添加一个例子,需要注意的是:和狗狗的例子不一样的是,方法虽然没有返回值,但是有参数,很简单,如图:
    在这里插入图片描述
    ③ 创建 InvocationHandler 接口的实现类,重写 invoke 方法,通过这种方式,客户端在通过代理类的对象调用方法 playBall() 时,就会自动调用下面的 invoke 方法invoke 方法里具体的参数我们在例子2优化代码时再讲解,先看简单的实现,如图:
    在这里插入图片描述
    ④ 测试类
    在这里插入图片描述
    ⑤ 看测试效果:
    报这个错,是因为我们方法针对有参无参不通用,无参可执行,有参报错,修改一下方法,让有参可执行即可
    在这里插入图片描述
    我们下面还有优化,所以此处就做了简单的调整,简单看个效果即可:
    在这里插入图片描述
    在这里插入图片描述
    ok,有参可执行,但是我们也看到了,此处可优化,下面我就针对参数以及返回值简单说说,请继续看下面优化后的例子……

3.1.2 JDK动态代理例子2

  • 直接看代码吧
    ① 狗狗里新增2个方法,一个如下三个方法
void playBall();//玩球
void eatFood(String food);//吃食物
List<String> likeFruit(String ...fruits);

逻辑没啥可说的,注意参数和返回值,直接看图:
在这里插入图片描述
② 再看小孩的:
在这里插入图片描述
③ 主要看 PublicPlayProxy2.java
在这里插入图片描述
注意下面解释 invoke 方法的参数以及返回值,以及根据业务类里方法的参数优化重写 invoke 优化。
在这里插入图片描述
④ 测试1
在这里插入图片描述
在这里插入图片描述
⑤ 测试2,都是一样的,这个简单看看即可
在这里插入图片描述

分析需优化的代码
  • 不知道读者有没用注意到,我们上面的例子在客户端测试的时候还是有代理类的出现,那么好像并没有很好地体现动态代理,我看《秒懂设计模式》书上,作者是这么个设计思路,但我没太明白,好像此处并没用动态生成代理对象呀,为啥就动态代理了?,如下图:
    在这里插入图片描述
  • 可能是看知识比较浅,对作者书本知识的理解的还不够透彻,但是不管怎么样,我还是想将上面的再给优化化一下,请继续往下看吧:

3.1.3 JDK动态代理例子3

  • 改动的地方:
    ① 新增 PublicPlayProxy3 (将 PublicPlayProxy2 改为 PublicPlayProxy3),但是内容几乎保持不变
    在这里插入图片描述
    ② 新增 MakeProxyInstance.java ,注意这是核心代码,体现动态性的,如图:
    在这里插入图片描述
    ③ 再看测试类 ClientJdkProxyTest3.java ,:
    在这里插入图片描述
    ④ 测试结果:
    在这里插入图片描述
分析体现的动态性
  • 对比上面的写法,这种优化后的实现方式可谓相当简单,整个过程代理类的代理对象没用出现,是程序执行的过程中动态创建的,所以这才体现了我们所说的动态性嘛!
  • ok,到这里算是完美了,相信看到这里的你应该已经非常明白了!

3.2 Cglib 代理

  • 今天先到这里,这个后续有空了再补上吧!

4. 附代码

4.1 静态代理代码

4.1 例子1代码

package com.liu.susu.design.pattern.proxy.statics.example2;

/**
 * @Description 测试静态代理1
 * @Author susu
 * @date 2022-02-11
 **/
public interface PetPlayInterface {
    void playBall();//玩球
}

/**
 * 被代理类:目标业务
 */
class DogPlay implements PetPlayInterface {
    @Override
    public void playBall() {
        System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
    }
}

/**
 * 代理类
 */
class PetPlayProxy implements PetPlayInterface {
    private DogPlay dogPlay;//被代理对象

    //①法: 通过有参构造传入被代理的实例化对象
    public PetPlayProxy(DogPlay dogPlay) {
        this.dogPlay = dogPlay;
    }

    //②法: 在无参构造器里直接实例化被代理对象
    public PetPlayProxy() {
        this.dogPlay = new DogPlay();
    }

    @Override
    public void playBall() {
        bugBall();//代理做的事  相当于在不改变被代理 playBall() 方法的前提下,扩展了功能
        dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
    }

    public void bugBall() {
        System.out.println("代理帮忙买球,卖玩球之后狗狗就可以玩了");
    }
}

/**
 * 测试类
 */
class ClientStaticProxyTest {

    public static void main(String[] args) {

        System.out.println("①法: 通过有参构造传入被代理的实例化对象");
        DogPlay dogPlay = new DogPlay();
        PetPlayProxy petPlayProxy = new PetPlayProxy(dogPlay);
        petPlayProxy.playBall();//即:使用的时候不需要被代理调用方法,直接代理对象执行(包括扩展的功能)

        System.out.println("\n②法: 在无参构造器里直接实例化被代理对象");
        PetPlayInterface petPlayInterface = new PetPlayProxy();//② 直接实例化代理对象
        petPlayInterface.playBall();

    }
}

4.2 例子2代码

package com.liu.susu.design.pattern.proxy.statics.example3;

/**
 * @Description 测试静态代理2 根据狗狗年龄判断买大小合适的球
 * @Author susu
 * @date 2022-02-11
 **/
public interface PetPlayInterface {
    void playBall();//玩球
}

/**
 * 被代理类:目标业务
 */
class DogPlay implements PetPlayInterface {

    @Override
    public void playBall() {
        System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
    }
}

/**
 * 代理类
 */
class PetPlayProxy implements PetPlayInterface {
    private DogPlay dogPlay;//被代理对象
    private Dogs dog;

    public PetPlayProxy() {

    }
    public PetPlayProxy(Dogs dog) {
        this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
        this.dog = dog;
    }
    @Override
    public void playBall() {
        bugBall(dog);
        dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
    }
    /**
     * 新增一个方法:根据狗的年龄判断买什么样的球
     */
    public void bugBall(Dogs dog) {
        if (dog.getAge()>1){
            System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",是成犬了,买大号球");
        }else {
            System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",还没成犬,买小号球吧");
        }
    }

}

class Dogs{
    private String name;
    private int age;

    public Dogs(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

/**
 * 测试类
 */
class ClientStaticProxyTest {

    public static void main(String[] args) {
        PetPlayInterface petPlayInterface = new PetPlayProxy(new Dogs("麦兜",2));
        petPlayInterface.playBall();
    }

}

4.2 动态代理代码

4.1 JDK 动态代理

4.1.1 JDK 动态代理例子1
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example1;

/**
 * @Description
 * @Author susu
 * @date 2022-02-11
 **/
public interface PetPlayInterface {
    void playBall();//玩球
}

/**
 * 被代理类1:DogPlay
 */
class DogPlay implements PetPlayInterface {

    @Override
    public void playBall() {
        System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
    }
}

/**
 * 狗狗代理类
 */
class PetPlayProxy implements PetPlayInterface {
    private DogPlay dogPlay;//被代理对象
    private Dogs dog;

    public PetPlayProxy() {

    }
    public PetPlayProxy(Dogs dog) {
        this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
        this.dog = dog;
    }
    @Override
    public void playBall() {
        bugBall(dog);
        dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
    }
    /**
     * 新增一个方法:根据狗的年龄判断买什么样的球
     */
    public void bugBall(Dogs dog) {
        if (dog.getAge()>1){
            System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",是成犬了,买大号球");
        }else {
            System.out.println("狗狗-->"+dog.getName()+"的年龄是:"+dog.getAge()+",还没成犬,买小号球吧");
        }
    }

}

class Dogs{
    private String name;
    private int age;

    public Dogs(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example1;

/**
 * @Description
 * @Author susu
 * @date 2022-02-11
 **/
public interface ChildPlayInterface {
    void playToyCar(String type);
}

/**
 * 被代理类2:ChildPlay
 */
class ChildPlay implements ChildPlayInterface{
    @Override
    public void playToyCar(String type) {
        System.out.println("小孩子喜欢玩玩具车,去玩球玩玩具车去了,好像是-->"+type);
    }
}
//代理类
class ChildPlayProxy implements ChildPlayInterface{
    private ChildPlay childPlay;
    public ChildPlayProxy(){
        childPlay = new ChildPlay();
    }
    @Override
    public void playToyCar(String type) {
        childPlay.playToyCar(type);
    }
}
4.1.2 JDK 动态代理例子2
  • 小孩相关的
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

/**
 * @Description
 * @Author susu
 * @date 2022-02-11
 **/
public interface ChildPlayInterface {
    void playToyCar();
    void eatFood(String food);//吃食物
    boolean likeFruit(String ...fruits);//喜欢的水果
}

/**
 * 被代理类2:ChildPlay
 */
class ChildPlay implements ChildPlayInterface{
    @Override
    public void playToyCar() {
        System.out.println("小孩子喜欢玩玩具车,去玩球玩具车去了……");
    }
    @Override
    public void eatFood(String food) {
        System.out.println("这个食物是:"+food+",小孩子很喜欢吃的……");
    }
    @Override
    public boolean likeFruit(String ...fruits) {
        boolean isLike = false;//是否有孩子喜欢的食物
        for (String fruit : fruits) {
            if ("apple".equals(fruit)){
                System.out.println("是苹果,是小孩子喜欢的水果……");
                isLike = true;
            }else if ("banana".equals(fruit)){
                System.out.println("是香蕉,是小孩子喜欢的水果……");
                isLike = true;
            }else if ("grape".equals(fruit)){
                System.out.println("是葡萄,太酸了,是有些小孩子不喜欢的水果……");
            }else{
                System.out.println("其他水果,不知道小孩子是否喜欢……");
            }
        }
        return isLike;
    }
}

class ChildPlayProxy implements ChildPlayInterface{
    private ChildPlay childPlay;

    public ChildPlayProxy(ChildPlay childPlay) {
        this.childPlay = childPlay;
    }

    @Override
    public void playToyCar() {
        childPlay.playToyCar();
    }

    @Override
    public void eatFood(String food) {
        childPlay.eatFood(food);
    }

    @Override
    public boolean likeFruit(String... fruits) {
        return childPlay.likeFruit(fruits);
    }
}
  • 狗狗相关的
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description
 * @Author susu
 * @date 2022-02-11
 **/
public interface PetPlayInterface {
    void playBall();//玩球
    void eatFood(String food);//吃食物
    List<String> likeFruit(String ...fruits);//喜欢的水果
}

/**
 * 被代理类1:DogPlay
 */
class DogPlay implements PetPlayInterface {

    @Override
    public void playBall() {
        System.out.println("狗狗喜欢玩球,狗狗又去玩球去了……");
    }

    @Override
    public void eatFood(String food) {
        System.out.println("这个食物是:"+food+",狗狗很喜欢吃的……");
    }

    @Override
    public List<String> likeFruit(String ...fruits) {
        List<String> likeFruitList = new ArrayList<String>();
        for (String fruit : fruits) {
            if ("apple".equals(fruit)){
                System.out.println("是苹果,是狗狗喜欢的水果……");
                likeFruitList.add(fruit);//把喜欢吃的水果返回
            }else if ("banana".equals(fruit)){
                System.out.println("是香蕉,狗狗不喜欢吃香蕉……");
            }else if ("pear".equals(fruit)){
                System.out.println("是梨,是狗狗喜欢的水果……");
                likeFruitList.add(fruit);//把喜欢吃的水果返回
            }else if ("banana".equals(fruit)){
                System.out.println("是香蕉,狗狗不喜欢吃香蕉……");
            }else if ("grape".equals(fruit)){
                System.out.println("是葡萄,狗狗是不能吃葡萄的,会中毒……");
            }else{
                System.out.println("未知水果,不知道狗狗能不能吃的水果,确认后再给狗狗吃……");
            }
        }
        return likeFruitList;
    }
}

/**
 * 狗狗代理类
 */
class PetPlayProxy implements PetPlayInterface {
    private DogPlay dogPlay;//被代理对象

    public PetPlayProxy() {
        this.dogPlay = new DogPlay();//① 构造器里直接实例化被代理对象
    }

    @Override
    public void playBall() {
        dogPlay.playBall();//目标对象要做的事(即:代理类将控制权移交给被代理类)
    }

    @Override
    public void eatFood(String food) {
        dogPlay.eatFood(food);
    }

    @Override
    public List<String> likeFruit(String... fruits) {
        return dogPlay.likeFruit(fruits);
    }

}

  • PublicPlayProxy2
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

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

/**
 * @FileName PublicPlayProxy2
 * @Description  通用的动态代理类2——处理方法有参或无参的情况
 * @Author susu
 * @date 2022-02-11
 **/
public class PublicPlayProxy2 implements InvocationHandler {

    private Object origin;//被代理的真实对象

    public PublicPlayProxy2() {
    }
    public PublicPlayProxy2(Object origin) {
        this.origin = origin;//方法① 通过构造器,注入被代理对象
    }

    public void bindOrigin(Object origin){
        this.origin = origin;//方法② 通过调用方法,进行绑定从而给被代理对象赋值
    }

    /**
     * 通过代理类的对象调用方法时,动态调用被代理类的同名方法
     * 客户端通过代理类的对象调用方法playBall()时,就会自动调用 invoke 方法
     * 即:将被代理类执行的方法playBall()的功能就声明在了invoke 方法中
    * @param proxy 动态获取的:被代理的代理类
	* @param method 动态获取的:动态要调用的方法
	* @param args 客户端调用时传来的参数值
    * @return java.lang.Object 动态调用方法 method 时的返回值作为该 invoke 方法的返回值
    * @Author susu
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("jdk动态代理,begin 被代理的实际业务……");

        Object result;
        if (args == null){
            result = method.invoke(origin);//无参数的方法
        }else {
            result = method.invoke(origin, args);//有参数的方法
        }
        System.out.println("方法的返回值 result 是--->"+result);
        System.out.println("jdk动态代理,end");
        return result;
    }
}

  • 测试类:
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

import org.junit.Test;

import java.lang.reflect.Proxy;

/**
 * @FileName ClientJdkProxyTest2  JDK动态代理测试类2
 * @Description
 * @Author susu
 * @date 2022-02-11
 **/
public class ClientJdkProxyTest2 {
    public static void main(String[] args) {
        /**************************测试狗狗的******************************************/
        // new PublicPlayProxy2(new PetPlayProxy()) 通过构造器注入被代理的真实对象
        PetPlayInterface petPlayInterface = (PetPlayInterface) Proxy.newProxyInstance(
                PetPlayProxy.class.getClassLoader(),
                PetPlayProxy.class.getInterfaces(),
                new PublicPlayProxy2(new PetPlayProxy())
        );
        System.out.println("===========① 测试狗狗 playBall(),代理============");
        petPlayInterface.playBall();

        System.out.println("\n===========② 测试狗狗 eatFood(String food),代理============");
        petPlayInterface.eatFood("火龙果");
        petPlayInterface.eatFood(null);

        System.out.println("\n===========③ 测试狗狗 likeFruit(String ...fruits),代理============");
        petPlayInterface.likeFruit("grape","apple","banana","pear");

    }

    @Test
    public void testChildProxy(){
        /**************************测试小孩的******************************************/
        ChildPlay childPlay = new ChildPlay();
        PublicPlayProxy2 publicPlayProxy2 = new PublicPlayProxy2();
        publicPlayProxy2.bindOrigin(childPlay);//通过 bindOrigin 方法注入被代理的真实对象

        ChildPlayInterface childPlayInterface = (ChildPlayInterface) Proxy.newProxyInstance(
                ChildPlayProxy.class.getClassLoader(),
                ChildPlayProxy.class.getInterfaces(),
                publicPlayProxy2
        );

        System.out.println("===========① 测试小孩 playBall(),代理============");
        childPlayInterface.playToyCar();

        System.out.println("\n===========② 测试小孩 eatFood(String food),代理============");
        childPlayInterface.eatFood("火龙果");
        childPlayInterface.eatFood(null);

        System.out.println("\n===========③ 测试小孩 likeFruit(String ...fruits),代理============");
        childPlayInterface.likeFruit("grape","apple","banana","pear");

    }
}

4.1.3 JDK 动态代理例子3
  • 在上面做的优化,所以只贴新增不一样的代码了
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

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

/**
 * @FileName PublicPlayProxy3
 * @Description  通用的动态代理类3
 * @Author susu
 * @date 2022-04-15
 **/
public class PublicPlayProxy3 implements InvocationHandler {

    private Object origin;//被代理的真实对象

    public PublicPlayProxy3(Object origin) {
        this.origin = origin;//方法① 通过构造器,注入被代理对象
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用被代理对象方法
        System.out.println("jdk动态代理,开始被代理的实际业务……");

        Object result ;
        if (args == null){
            result = method.invoke(origin);//无参数的方法
        }else {
            result = method.invoke(origin,args);//有参数的方法
        }
        System.out.println("jdk动态代理,结束");

        System.out.println("方法的返回值 result 是--->"+result);
        return result;
    }

}

/**
 * 用JDK动态代理时,获取动态代理实例
 */
class MakeProxyInstance{
    /**
    * @Description: 动态获取一个代理类对象:根据被代理类获取代理类对象
    * @param object 被代理类
    * @return java.lang.Object
    * @Author susu
    */
    public static Object getProxyInstance(Object object){
        PublicPlayProxy3 publicPlayProxy3 = new PublicPlayProxy3(object);

        Object proxyInstance = Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                publicPlayProxy3
        );
        return proxyInstance;
    }
}
package com.liu.susu.design.pattern.proxy.dynamic.jdk.example2;

/**
 * @FileName ClientJdkProxyTest3
 * @Description   JDK动态代理测试类3——测试通过动态获取代理类
 * @Author susu
 * @date 2022-02-11
 **/
public class ClientJdkProxyTest3 {

    public static void main(String[] args) {
        DogPlay dogPlay = new DogPlay();
        /**
         * 通过代理类,获取代理类的对象
         * 动态获取代理类的动态性体现在:整个执行过程没用出现代理类
         */
        PetPlayInterface proxyInstance
                = (PetPlayInterface) MakeProxyInstance.getProxyInstance(dogPlay);
        proxyInstance.likeFruit("apple","grape");

        System.out.println("\n======================\n");

        ChildPlay childPlay = new ChildPlay();
        ChildPlayInterface childPlayInterface
                = (ChildPlayInterface) MakeProxyInstance.getProxyInstance(childPlay);
        childPlayInterface.likeFruit("apple","grape");
    }

}

4.2 Cglib 代理

5. 打包源码

Java设计模式——代理设计模式(静态代理和动态代理).

6. 参考书籍

  • 书籍:刘韬 版的《秒懂设计模式》;
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@素素~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值