refresh() -> invokeBeanFactoryPostProcessors()

refresh() -> invokeBeanFactoryPostProcessors()

调用各种BeanFactory处理器,如:

解析占位符${}中对应属性值的替换工作

扩充

通常如果想对BeanFactory中的数据进行修改时可以通过实现BeanFactoryPostProcessor来进行实现

@FunctionalInterface
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

针对BeanFactory

同理,如果想要修改BeanDefinition中的信息时可以实现BeanDefinitionRegistryPostProcessor进行操作

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

针对BeanDefinition,而BeanFactory已包含;BeanDefinitionRegistry的作用为对BeanDefinition的增删改查操作

如果一个类实现了BeanDefinitionRegistryPostProcessor,那么他的postProcessBeanFactory()方法可以跟其他的BeanFactoryPostProcessor一起执行

所有后置Registry的对象都是对前面名称对象的增删改查的操作

有关IOC中对象执行顺序的说明

PriorityOrdered > Ordered > 没有实现任何排序接口的对象

public interface PriorityOrdered extends Ordered {
}

public interface Ordered {
	int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
	int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
	int getOrder();
}

invokeBeanFactoryPostProcessors()

实例化并且调用所有已经注册了的BeanFactoryPostProcessor,遵循指明的顺序

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  // 获取到当前应用程序上下文的beanFactoryPostProcessors变量的值,并且实例化调用执行所有已经注册的beanFactoryPostProcessor
  PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

  // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
  // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
  if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  }
}

getBeanFactoryPostProcessors()

默认情况下,通过此来获取已经注册的BFPP(BeanFactoryPostProcessor),但是默认是空的,此处也可以将自定义的BFPP获取到

自定义BFPP
  1. 实现BFPP接口

    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            System.out.println("自定义BeanFactoryPostProcessor");
        }
    }
    
  2. 将自定义BFPP注入到IOC容器或者自定义ClassPathXmlApplicationContext重写在getBeanFactoryPostProcessors()调用之前的任意方法调用父类的addBeanFactoryPostProcessor()进行BFPP的注入,如:customizeBeanFactory()

    import com.armin.self_bfpp.MyBeanFactoryPostProcessor;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyClassPathApplicationContext extends ClassPathXmlApplicationContext {
        public MyClassPathApplicationContext(String... configLocations) throws BeansException {
            super(configLocations);
        }
        @Override
        protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
            super.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
            super.customizeBeanFactory(beanFactory);
        }
    }
    

在这里插入图片描述

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()

实例化调用执行所有已经注册的beanFactoryPostProcessor

public static void invokeBeanFactoryPostProcessors(
  ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

  // Invoke BeanDefinitionRegistryPostProcessors first, if any.
  // 无论是什么情况,优先执行BeanDefinitionRegistryPostProcessors
  // 将已经执行过的BFPP存储在processedBeans中,防止重复执行
  Set<String> processedBeans = new HashSet<>();

  // 判断beanfactory是否是BeanDefinitionRegistry类型,此处是DefaultListableBeanFactory,实现了BeanDefinitionRegistry接口,所以为true
  if (beanFactory instanceof BeanDefinitionRegistry) {
    // 类型转换
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    // 此处希望大家做一个区分,两个接口是不同的,BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子集
    // BeanFactoryPostProcessor主要针对的操作对象是BeanFactory,而BeanDefinitionRegistryPostProcessor主要针对的操作对象是BeanDefinition
    // 存放BeanFactoryPostProcessor的集合
    List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
    // 存放BeanDefinitionRegistryPostProcessor的集合
    List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

    // 首先处理入参中的beanFactoryPostProcessors,遍历所有的beanFactoryPostProcessors,将BeanDefinitionRegistryPostProcessor
    // 和BeanFactoryPostProcessor区分开
    for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
      // 如果是BeanDefinitionRegistryPostProcessor
      if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
        BeanDefinitionRegistryPostProcessor registryProcessor =
          (BeanDefinitionRegistryPostProcessor) postProcessor;
        // 直接执行BeanDefinitionRegistryPostProcessor接口中的postProcessBeanDefinitionRegistry方法
        registryProcessor.postProcessBeanDefinitionRegistry(registry);
        // 添加到registryProcessors,用于后续执行postProcessBeanFactory方法
        registryProcessors.add(registryProcessor);
      } else {
        // 否则,只是普通的BeanFactoryPostProcessor,添加到regularPostProcessors,用于后续执行postProcessBeanFactory方法
        regularPostProcessors.add(postProcessor);
      }
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
    // Separate between BeanDefinitionRegistryPostProcessors that implement
    // PriorityOrdered, Ordered, and the rest.
    // 用于保存本次要执行的BeanDefinitionRegistryPostProcessor
    List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

    // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
    // 调用所有实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor实现类
    // 找到所有实现BeanDefinitionRegistryPostProcessor接口bean的beanName
    String[] postProcessorNames =
      beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    // 遍历处理所有符合规则的postProcessorNames
    for (String ppName : postProcessorNames) {
      // 检测是否实现了PriorityOrdered接口
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        // 获取名字对应的bean实例,添加到currentRegistryProcessors中
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        // 将要被执行的BFPP名称添加到processedBeans,避免后续重复执行
        processedBeans.add(ppName);
      }
    }
    // 按照优先级进行排序操作
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    // 添加到registryProcessors中,用于最后执行postProcessBeanFactory方法
    registryProcessors.addAll(currentRegistryProcessors);
    // 遍历currentRegistryProcessors,执行postProcessBeanDefinitionRegistry方法
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    // 执行完毕之后,清空currentRegistryProcessors
    currentRegistryProcessors.clear();

    // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
    // 调用所有实现Ordered接口的BeanDefinitionRegistryPostProcessor实现类
    // 找到所有实现BeanDefinitionRegistryPostProcessor接口bean的beanName,
    // 此处需要重复查找的原因在于上面的执行过程中可能会新增其他的BeanDefinitionRegistryPostProcessor
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
      // 检测是否实现了Ordered接口,并且还未执行过
      if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        // 获取名字对应的bean实例,添加到currentRegistryProcessors中
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        // 将要被执行的BFPP名称添加到processedBeans,避免后续重复执行
        processedBeans.add(ppName);
      }
    }
    // 按照优先级进行排序操作
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    // 添加到registryProcessors中,用于最后执行postProcessBeanFactory方法
    registryProcessors.addAll(currentRegistryProcessors);
    // 遍历currentRegistryProcessors,执行postProcessBeanDefinitionRegistry方法
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    // 执行完毕之后,清空currentRegistryProcessors
    currentRegistryProcessors.clear();

    // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
    // 最后,调用所有剩下的BeanDefinitionRegistryPostProcessors
    boolean reiterate = true;
    while (reiterate) {
      reiterate = false;
      // 找出所有实现BeanDefinitionRegistryPostProcessor接口的类
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      // 遍历执行
      for (String ppName : postProcessorNames) {
        // 跳过已经执行过的BeanDefinitionRegistryPostProcessor
        if (!processedBeans.contains(ppName)) {
          // 获取名字对应的bean实例,添加到currentRegistryProcessors中
          currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          // 将要被执行的BFPP名称添加到processedBeans,避免后续重复执行
          processedBeans.add(ppName);
          reiterate = true;
        }
      }
      // 按照优先级进行排序操作
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      // 添加到registryProcessors中,用于最后执行postProcessBeanFactory方法
      registryProcessors.addAll(currentRegistryProcessors);
      // 遍历currentRegistryProcessors,执行postProcessBeanDefinitionRegistry方法
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      // 执行完毕之后,清空currentRegistryProcessors
      currentRegistryProcessors.clear();
    }

    // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
    // 调用所有BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
    invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
    // 最后,调用入参beanFactoryPostProcessors中的普通BeanFactoryPostProcessor的postProcessBeanFactory方法
    invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  } else {
    // Invoke factory processors registered with the context instance.
    // 如果beanFactory不归属于BeanDefinitionRegistry类型,那么直接执行postProcessBeanFactory方法
    invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
  }

  // 到这里为止,入参beanFactoryPostProcessors和容器中的所有BeanDefinitionRegistryPostProcessor已经全部处理完毕,下面开始处理容器中
  // 所有的BeanFactoryPostProcessor
  // 可能会包含一些实现类,只实现了BeanFactoryPostProcessor,并没有实现BeanDefinitionRegistryPostProcessor接口

  // Do not initialize FactoryBeans here: We need to leave all regular beans
  // uninitialized to let the bean factory post-processors apply to them!
  // 找到所有实现BeanFactoryPostProcessor接口的类
  String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

  // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
  // Ordered, and the rest.
  // 用于存放实现了PriorityOrdered接口的BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  // 用于存放实现了Ordered接口的BeanFactoryPostProcessor的beanName
  //		List<String> orderedPostProcessorNames = new ArrayList<>();
  List<BeanFactoryPostProcessor> orderedPostProcessor = new ArrayList<>();
  // 用于存放普通BeanFactoryPostProcessor的beanName
  //		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  List<BeanFactoryPostProcessor> nonOrderedPostProcessorNames = new ArrayList<>();
  // 遍历postProcessorNames,将BeanFactoryPostProcessor按实现PriorityOrdered、实现Ordered接口、普通三种区分开
  for (String ppName : postProcessorNames) {
    // 跳过已经执行过的BeanFactoryPostProcessor
    if (processedBeans.contains(ppName)) {
      // skip - already processed in first phase above
    }
    // 添加实现了PriorityOrdered接口的BeanFactoryPostProcessor到priorityOrderedPostProcessors
    else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
      priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    }
    // 添加实现了Ordered接口的BeanFactoryPostProcessor的beanName到orderedPostProcessorNames
    else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
      //				orderedPostProcessorNames.add(ppName);
      orderedPostProcessor.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    } else {
      // 添加剩下的普通BeanFactoryPostProcessor的beanName到nonOrderedPostProcessorNames
      //				nonOrderedPostProcessorNames.add(ppName);
      nonOrderedPostProcessorNames.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    }
  }

  // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
  // 对实现了PriorityOrdered接口的BeanFactoryPostProcessor进行排序
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  // 遍历实现了PriorityOrdered接口的BeanFactoryPostProcessor,执行postProcessBeanFactory方法
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
  // 创建存放实现了Ordered接口的BeanFactoryPostProcessor集合
  //		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  // 遍历存放实现了Ordered接口的BeanFactoryPostProcessor名字的集合
  //		for (String postProcessorName : orderedPostProcessorNames) {
  // 将实现了Ordered接口的BeanFactoryPostProcessor添加到集合中
  //			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  //		}
  // 对实现了Ordered接口的BeanFactoryPostProcessor进行排序操作
  //		sortPostProcessors(orderedPostProcessors, beanFactory);
  sortPostProcessors(orderedPostProcessor, beanFactory);
  // 遍历实现了Ordered接口的BeanFactoryPostProcessor,执行postProcessBeanFactory方法
  //		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessor, beanFactory);

  // Finally, invoke all other BeanFactoryPostProcessors.
  // 最后,创建存放普通的BeanFactoryPostProcessor的集合
  //		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  // 遍历存放实现了普通BeanFactoryPostProcessor名字的集合
  //		for (String postProcessorName : nonOrderedPostProcessorNames) {
  // 将普通的BeanFactoryPostProcessor添加到集合中
  //			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  //		}
  // 遍历普通的BeanFactoryPostProcessor,执行postProcessBeanFactory方法
  //		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessorNames, beanFactory);

  // Clear cached merged bean definitions since the post-processors might have
  // modified the original metadata, e.g. replacing placeholders in values...
  // 清除元数据缓存(mergeBeanDefinitions、allBeanNamesByType、singletonBeanNameByType)
  // 因为后置处理器可能已经修改了原始元数据,例如,替换值中的占位符
  beanFactory.clearMetadataCache();
}

整个方法的大致执行流程

  1. 首先执行beanFactory调用addBeanFactoryPostProcessor添加的BeanFactoryPostProcessor

    如果该类为BeanDefinitionRegistryPostProcessor的实现先执行BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法再将该实例放入集合中后续统一执行父接(BeanFactoryPostProcessor)口中的postProcessBeanFactory方法

  2. BeanFactory中获取BeanDefinitionRegistryPostProcessor并且实现PriorityOrdered接口的BeanName,然后通过BeanFactory<T> T getBean(String name, Class<T> requiredType)方法获取实例(该方法会调用实例化Bean中的creatBean方法),放入集合先统一执行postProcessBeanDefinitionRegistry再将该对象放入另一个集合后续统一执行postProcessBeanFactory方法

  3. 再次重复第2点(不一样的地方:PriorityOrdered改为Ordered

  4. 执行剩下的BeanDefinitionRegistryPostProcessor考虑可能存在执行上一步的时候在BeanDefinitionRegistryPostProcessor 对象中调用postProcessBeanDefinitionRegistry会添加新的BeanDefinitionRegistryPostProcessor 对象此处会循环将所有的BeanDefinitionRegistryPostProcessor 执行完

  5. 获取BeanFactoryPostProcessor类型的所有BeanName根据是否实现PriorityOrdered,Ordered和没有实现任何排序接口的依次从前往后执行postProcessBeanFactory方法

invokeBeanDefinitionRegistryPostProcessors()

调用给定BeanDefinitionRegistryPostProcessor Bean对象

private static void invokeBeanDefinitionRegistryPostProcessors(
  Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

  //遍历 postProcessors
  for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
    //调用 postProcessor 的 postProcessBeanDefinitionRegistry以使得postProcess往registry注册BeanDefinition对象
    postProcessor.postProcessBeanDefinitionRegistry(registry);
  }
}

invokeBeanFactoryPostProcessors()

调用给定的BeanFactoryPostProcessor类型Bean对象

private static void invokeBeanFactoryPostProcessors(
  Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

  //遍历postProcessors
  for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    //回调 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法,使得每个postProcessor对象都可以对
    // beanFactory进行调整
    postProcessor.postProcessBeanFactory(beanFactory);
  }
}

测试从BeanFactory中根据类型获取到的BeanDefinitionRegisterPostProcessor在其中又添加新的BeanDefinitionRegisterPostProcessor执行结果

MyBeanDefinitionRegisterPostProcessor

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

public class MyBeanDefinitionRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("执行postProcessBeanDefinitionRegistry()--" + getClass().getName());
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(MySelfBeanDefinitionRegisterPostProcessor.class);
        builder.addPropertyValue("name", "armin");
        registry.registerBeanDefinition("mySelfBdrpp",builder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("执行postProcessBeanFactory()--" + getClass().getName());
    }
}

MySelfBeanDefinitionRegisterPostProcessor

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

public class MySelfBeanDefinitionRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("执行postProcessBeanDefinitionRegistry()--" + getClass().getName());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("执行postProcessBeanFactory()--" + getClass().getName());
    }
}

Test

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("armin-self-bdrpp.xml");
    }

}

armin-self-bdrpp.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.armin.self_bdrpp.MyBeanDefinitionRegisterPostProcessor"></bean>

</beans>

执行结果

> Task :spring-debug:Test.main()
执行postProcessBeanDefinitionRegistry()--com.armin.self_bdrpp.MyBeanDefinitionRegisterPostProcessor
执行postProcessBeanDefinitionRegistry()--com.armin.self_bdrpp.MySelfBeanDefinitionRegisterPostProcessor
执行postProcessBeanFactory()--com.armin.self_bdrpp.MyBeanDefinitionRegisterPostProcessor
执行postProcessBeanFactory()--com.armin.self_bdrpp.MySelfBeanDefinitionRegisterPostProcessor

BUILD SUCCESSFUL in 2m 1s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值