Spring容器初始化之BeanFactoryPostProcessor和BeanPostProcessor

本文详细分析了Spring容器初始化时`refresh()`方法中的重要步骤,包括`invokeBeanFactoryPostProcessors()`和`registerBeanPostProcessors()`。`invokeBeanFactoryPostProcessors()`主要处理`BeanFactoryPostProcessor`,尤其是`BeanDefinitionRegistryPostProcessor`,允许在启动期间动态修改BeanDefinition。`registerBeanPostProcessors()`则负责注册`BeanPostProcessor`,为后续的扩展点提供基础。这两个扩展点为开发者提供了自定义行为的可能。

        本文仍然以ClasspathXmlApplicationContext为入口进行记录,通过之前对Spring的学习,我们已知ClasspathXmlApplicationContext构造方法中调用的this.refresh()方法是整个Spring容器初始化的核心方法,refresh()方法调用过程如下:

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
//重要方法1                
this.invokeBeanFactoryPostProcessors(beanFactory);
//重要方法2
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }

                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
                contextRefresh.end();
            }

        }
    }

一、invokeBeanFactoryPostProcessors(beanFactory)

        进入refresh()方法,先来看这个方法调用:this.invokeBeanFactoryPostProcessors(beanFactory);这个方法的作用顾名思义,就是调用BeanFactory中存在的BeanFactoryPostProcessor,该方法实现了在Spring 启动期间,能够对BeanDefinition进行操作。在该方法内部又调用了这个方法:PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());核心操作都在这个方法里,具体实现如下:

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
        Set<String> processedBeans = new HashSet();
        ArrayList regularPostProcessors;
        ArrayList registryProcessors;
        int var9;
        ArrayList currentRegistryProcessors;
        String[] postProcessorNames;
        //判断BeanFactory是否是BeanDefinitionRegistry类型,如果是,走下面的逻辑
        //通过继承关系可以看出,BeanFactory就是BeanDefinitionRegistry类型
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
            regularPostProcessors = new ArrayList();
            registryProcessors = new ArrayList();
        //此处beanFactoryPostProcessors集合为空,因为此时还没有BeanFactoryPostProcessor加入到这个集合中。
            Iterator var6 = beanFactoryPostProcessors.iterator();
        //由于beanFactoryPostProcessors集合为空,因此这段while逻辑不会执行
            while(var6.hasNext()) {
                BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                } else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            currentRegistryProcessors = new ArrayList();
        //获取BeanFactory中类型是BeanDefinitionRegistryPostProcessor的类名
        //这里是能获取到有且只有一个,刚好是在前面<component-scan>标签解析最后,执行注册组件操作中加入到BeanDefinitionRegsitry中的ConfigurationClassPostProcessor
            postProcessorNames =         beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            String[] var16 = postProcessorNames;
            var9 = postProcessorNames.length;

            int var10;
            String ppName;
            for(var10 = 0; var10 < var9; ++var10) {
                ppName = var16[var10];
        //判断当前的BeanPostProcessor是否是PriorityOrdered类型
        //ConfigurationClassPostProcessor实现了PriorityOrdered接口,因此会进入这个if块
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        //进入if后,调用beanFactory.getBean()提前将ConfigurationClassPostProcessor实例化
        //将实例加入到了currentRegistryProcessors集合中
                            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
        //对BeanFactoryPostProcessor进行排序
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
        //执行BeanDefinitionRegistryPostProcessor接口提供的postProcessBeanDefinitionRegistry()方法
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            var16 = postProcessorNames;
            var9 = postProcessorNames.length;

            for(var10 = 0; var10 < var9; ++var10) {
                ppName = var16[var10];
        //继续判断BeanPostProcessor是否实现了Ordered接口
        //ConfigurationClassPostProcessor没有实现Ordered接口,因此不会加入currentRegistryProcessors集合
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        //processedBeans记录了已经处理过的类
                    processedBeans.add(ppName);
                }
            }
        //此时currentRegistryProcessors集合中为空
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
            boolean reiterate = true;

            while(reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                String[] var19 = postProcessorNames;
                var10 = postProcessorNames.length;

                for(int var26 = 0; var26 < var10; ++var26) {
                    String ppName = var19[var26];
        //剩下的就是既没有实现PriorityOrdered接口,也没有实现Ordered接口的类
        //且不在processedBeans集合中,意味着在前面没有处理过,在这里处理
                    if (!processedBeans.contains(ppName)) {
                        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                        processedBeans.add(ppName);
                        reiterate = true;
                    }
                }

                sortPostProcessors(currentRegistryProcessors, beanFactory);
                registryProcessors.addAll(currentRegistryProcessors);
                invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
                currentRegistryProcessors.clear();
            }
        //BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry()方法执行完了
        //接下来就是执行BeanFactoryPostProcessor中的postProcessBeanFactory()方法
        //由于BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor,因此这个类里也有这个方法
            invokeBeanFactoryPostProcessors((Collection)registryProcessors, (ConfigurableListableBeanFactory)beanFactory);
            invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        } else {
            invokeBeanFactoryPostProcessors((Collection)beanFactoryPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        }

        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
        regularPostProcessors = new ArrayList();
        registryProcessors = new ArrayList();
        currentRegistryProcessors = new ArrayList();
        postProcessorNames = postProcessorNames;
        int var20 = postProcessorNames.length;

        String ppName;
        for(var9 = 0; var9 < var20; ++var9) {
            ppName = postProcessorNames[var9];
            if (!processedBeans.contains(ppName)) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    regularPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    registryProcessors.add(ppName);
                } else {
                    currentRegistryProcessors.add(ppName);
                }
            }
        }

        sortPostProcessors(regularPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList();
        Iterator var21 = registryProcessors.iterator();

        while(var21.hasNext()) {
            String postProcessorName = (String)var21.next();
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }

        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors((Collection)orderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList();
        Iterator var24 = currentRegistryProcessors.iterator();

        while(var24.hasNext()) {
            ppName = (String)var24.next();
            nonOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }

        invokeBeanFactoryPostProcessors((Collection)nonOrderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        beanFactory.clearMetadataCache();
    }

        上面方法的核心就是实例化容器中的BeanFactoryPostProcessor,此时是BeanDefinitionRegistryPostProcessor,实际上就是之前在注册组件时加入进来的ConfigurationClassPostProcessor,这个类会在后面基于注解的Spring初始化流程中重点说明,它实现了BeanDefinitionRegistryPostProcessor接口,并依次调用接口需要实现的两个方法,分别为:postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)和postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。

        这里需要着重记录的是,此处为Spring为我们提供的一个扩展点,即我们可以自己写一个类实现BeanDefinitionRegistryPostProcessor接口,并实现这两个方法,第一个方法传入了BeanDefinitionRegistry对象,我们知道这个对象为Spring的BeanDefinition注册器,能够对bd进行增删查改,这就使我们能够在程序启动过程中动态完成对某些bean的实例化或者修改操作。

        例如:我们实现如下一个类:

/**
* 这是我们自己实现的一个类,实现了BeanDefinitionRegistryPostProcessor接口
*/
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    //首先会调到这个方法里,并传入了registry对象,该对象中提供了对bd的各种操作
    //可以通过getBeanDefinition(String)方法获取某个待实例化类的bd对象,对里面的属性进行修改
        BeanDefinition bd = registry.getBeanDefinition("student");
        GenericBeanDefinition gbd = (GenericBeanDefinition) bd;
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("username" , "tom");
        gbd.setPropertyValues(propertyValues);
    //也可以new出一个bd对象,将某个类变为bd对象,这样在spring进行实例化的时候,就会把该对象也加入到spring容器中
    //这样我们就又多了一种能够实例化springbean的方式,并且是在程序启动过程中动态生成的。
        GenericBeanDefinition gbd2 = new GenericBeanDefinition();
        gbd2.setBeanClass(Teacher.class);
        propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("name" , "Jerry");
        propertyValues.addPropertyValue("age" , 33);
        gbd2.setPropertyValues(propertyValues);
        registry.registerBeanDefinition("teacher" , gbd2);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    //然后会调用到这个方法中,这个方法中传入了configurableListableBeanFactory对象
    //其实这个类也是实现了BeanDefinitionRegistry接口。
    }
}

 二registerBeanPostProcessors(beanFactory)

         Spring容器初始化第二个重要的步骤,将BeanPostProcessor注册到BeanFactory中,BeanPostProcessor为我们提供了非常方便的扩展机制,在n个地方都为我们埋了扩展点,方便我们进行扩展。我们进入该方法,内部又进行了一步方法调用:PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);我们重点来看这个方法的实现过程:

public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    //首先获取所有beanFactory中实现了BeanPostProcessor接口的类名
    //这里就是之前在注册组件时注册进来的两个分别为:
    //AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new PostProcessorRegistrationDelegate.BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    //new出这几个list是为了后面排序做准备
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList();
        List<String> orderedPostProcessorNames = new ArrayList();
        List<String> nonOrderedPostProcessorNames = new ArrayList();
        String[] var8 = postProcessorNames;
        int var9 = postProcessorNames.length;

        String ppName;
        BeanPostProcessor pp;
    //遍历所有实现上面接口的类名
        for(int var10 = 0; var10 < var9; ++var10) {
            ppName = var8[var10];
        //判断该类是否实现了PriorityOrdered接口
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        //如果实现了PriorityOrdered接口,调用beanFactory.getBean()提前将该bean实例化,变为spring的bean
                pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
        //实现了PriorityOrdered接口的BeanPostProcessor实例都放到了priorityOrderedPostProcessors集合中
        //两个BeanPostProcessor都实现了该接口,因此都被加入priorityOrderedPostProcessors集合
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
        //这两个BeanPostProcessor也都实现了MergedBeanDefinitionPostProcessor,因此也都被加入internalPostProcessors集合中
                    internalPostProcessors.add(pp);
                }
        //判断是否实现了Ordered接口,这里没有,因此这个集合是空的
            } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            } else {
        //这个集合也是空的
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        //对上面两个进行排序,排序后common在前面,autowired在后面
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        //按顺序注册到beanFactory中,其实就是在beanFactory内部维护了一个beanPostProcessors的list,此时注册BeanPostProcessor的工作已经完成。
        registerBeanPostProcessors(beanFactory, (List)priorityOrderedPostProcessors);
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList();
        Iterator var14 = orderedPostProcessorNames.iterator();
        //orderedPostProcessorNames这个集合是空的,因此不会走下面的循环
        while(var14.hasNext()) {
            String ppName = (String)var14.next();
            BeanPostProcessor pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }

        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, (List)orderedPostProcessors);
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList();
        Iterator var17 = nonOrderedPostProcessorNames.iterator();
        //这个集合也是空的,循环也不会走
        while(var17.hasNext()) {
            ppName = (String)var17.next();
            pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }

        registerBeanPostProcessors(beanFactory, (List)nonOrderedPostProcessors);
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, (List)internalPostProcessors);
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }

         该方法执行后就完成了BeanPostProcessor的注册工作,此时只有两个,即代码注释中提到的,另外,这里只是BeanPostProcessor的注册,还没有调用,在后面的过程中,会在各种不同的时机,对不同类型的BeanPostProcessor进行调用。

        还需要着重记录一下,这里便是Spring为我们提供的另一个扩展点,即自己写一个类实现BeanPostProcessor接口,BeanPostProcessor的子接口类型有很多,都是在不同的阶段调用的,在后面我们遇到的时候都会进行记录和总结,以便我们在今后的开发中能够灵活的对Spring进行扩展。

<think> 我们首先需要描述Spring容器初始化的主要步骤,然后将其转化为流程图。由于这里无法直接绘制图形,我将用文字描述流程图的结构,并尝试用Mermaid语法来绘制流程图(如果支持的话)。不过根据要求,我们主要用文字描述,并在最后提供Mermaid代码供参考。 Spring容器初始化过程(以ClassPathXmlApplicationContext为例)的主要步骤: 1. 资源定位:找到配置文件(如applicationContext.xml)的位置。 2. 加载解析配置:读取配置文件,解析成Bean定义(BeanDefinition)。 3. 注册Bean定义:将解析得到的BeanDefinition注册到BeanFactory中(具体的注册中心,如DefaultListableBeanFactory)。 4. BeanFactory后处理:调用BeanFactoryPostProcessor对Bean定义进行修改(例如PropertyPlaceholderConfigurer用来替换占位符)。 5. 实例化Bean:根据Bean定义创建Bean实例(单例的Bean在此步骤会被实例化)。 6. 依赖注入:填充Bean的属性,解决依赖(通过setter或构造器注入)。 7. Bean后处理:调用BeanPostProcessor对Bean实例进行增强(如AOP代理)。 8. 初始化:调用初始化方法(如afterPropertiesSetinit-method)。 9. 容器就绪:此时容器初始化完成,可以使用。 流程图可以简化为以下步骤: 开始 -> 资源定位 -> 加载解析配置 -> 注册Bean定义 -> BeanFactory后处理 -> 实例化Bean -> 依赖注入 -> Bean后处理 -> 初始化 -> 容器就绪 -> 结束 注意:其中实例化Bean、依赖注入、Bean后处理初始化是对于每个Bean而言的,但流程图通常展示主要流程。 由于要求用流程图,我们可以用文本描述流程图,并用Mermaid语法(如果支持)来绘制。这里我将用Mermaid语法描述,然后提供文字描述。 Mermaid流程图代码: ```mermaid graph TD A[开始] --> B[资源定位] B --> C[加载解析配置] C --> D[注册Bean定义到BeanFactory] D --> E[执行BeanFactoryPostProcessor] E --> F[实例化单例Bean] F --> G[依赖注入] G --> H[执行BeanPostProcessor的前置方法] H --> I[调用初始化方法] I --> J[执行BeanPostProcessor的后置方法] J --> K[容器就绪] K --> L[结束] ``` 注意:实际流程中,实例化Bean依赖注入是交织的(因为依赖注入可能触发其他Bean的实例化),但为了简化,我们展示主要步骤。 由于我们无法在文本中直接显示图形,这里用文字描述流程图: 开始 ↓ 资源定位(查找配置文件) ↓ 加载解析配置(将配置文件解析成BeanDefinition对象) ↓ 注册Bean定义(将BeanDefinition注册到BeanFactory的beanDefinitionMap中) ↓ 执行BeanFactoryPostProcessor(对BeanDefinition进行修改) ↓ 实例化单例Bean(非懒加载的单例Bean开始实例化) ↓ 依赖注入(填充Bean的属性,解决依赖) ↓ 执行BeanPostProcessor的前置方法(对Bean进行增强,在初始化前) ↓ 调用初始化方法(如InitializingBean的afterPropertiesSet或自定义init-method) ↓ 执行BeanPostProcessor的后置方法(在初始化后,如AOP代理) ↓ 容器就绪(可以获取Bean使用) ↓ 结束 注意:上述流程是简化的,实际Spring容器初始化过程还涉及很多细节,如循环依赖的处理、多级缓存等。 §§相关问题§§ 1. Spring容器初始化过程中,BeanFactoryPostProcessorBeanPostProcessor有什么区别? 2.Spring中,如何处理循环依赖的问题? 3. Spring容器初始化过程中,Bean的生命周期具体包括哪些步骤? 4. 如何自定义一个BeanPostProcessor,并描述它的作用时机? 5.Spring中,懒加载(lazy-init)是如何影响Bean的初始化的? </think> Spring容器初始化流程如下: ```mermaid graph TD A[启动容器] --> B[资源定位] B --> C[加载解析配置] C --> D[注册BeanDefinition] D --> E[BeanFactory后处理] E --> F[实例化单例Bean] F --> G[依赖注入] G --> H[Bean初始化] H --> I[容器就绪] ``` **核心步骤详解:** 1. **资源定位**:定位配置文件(XML/注解/JavaConfig) 2. **加载解析配置**:将配置解析为`BeanDefinition`对象 3. **注册BeanDefinition**:存入`BeanFactory`的注册表(`beanDefinitionMap`) 4. **BeanFactory后处理**:执行`BeanFactoryPostProcessor`(如属性占位符替换) 5. **实例化Bean**:对非懒加载的单例Bean进行实例化 6. **依赖注入**:通过Setter/构造器注入依赖对象 7. **Bean初始化**: - 调用`Aware`接口方法(BeanNameAware等) - 执行`BeanPostProcessor#postProcessBeforeInitialization` - 调用初始化方法(`@PostConstruct`/`InitializingBean`/`init-method`) - 执行`BeanPostProcessor#postProcessAfterInitialization` 8. **容器就绪**:完成初始化,可通过`getBean()`获取对象 **关键特性:** - 单例Bean默认在容器启动时创建 - 通过`BeanPostProcessor`实现AOP等扩展 - 循环依赖通过三级缓存解决(singletonFactories/earlySingletonObjects/singletonObjects)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值