【springboot】静态方法读取yml配置文件,可根据不同环境动态读取

SpringBoot获取YAML配置文件中的属性值(一):使用@Value注解、@ConfigurationProperties注解 SpringBoot支持使用Properties和YAML两种配置方式。两者功能类似,都能完成SpringBoot的配置,但是Properties的优先级要高于YAML(YAML语言的文件以“.yml”为后缀)。 使用application.yml文件配置SpringBoot项目。主要对SpringBoot项目的端口号、超时时间、参数值等进行配置。YAML文件的好处是——它采用的是树形结构,一目了然。如下: 阅读详情
AI摘要

本文介绍了在SpringBoot项目中,如何解决因多个模块共用同一配置导致的启动问题。通过创建一个工具类,动态读取当前环境对应的.yml配置文件,实现了在不同环境下正确加载配置,同时避免了未使用配置模块的启动错误。通过SpringContextUtil获取当前环境,再结合YamlPropertiesFactoryBean加载特定环境的配置文件,成功实现了静态方法读取配置。

springboot中读取配置文件的方式有很多种,我这里就不在叙述了;

背景:项目的common中有一个redis的工具类,因为很多模块用,不想在每个模块中都写一个redisconfig,并且使用的是工具类,静态加载的方式使用,但是如果用传统的读取方式如下:

RedisConfig:

package com.xx.xx.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedisConfig {

	// Redis服务器IP
	public static String IP;

	。。。。。。

	public String getIP() {
		return IP;
	}

	@Value("${redis.ip}")
	public void setIP(String ip) {
		IP = ip;
	}
}

RedisUtil:

/**
 * Redis 工具类
 */

public class RedisUtil {

	// protected static ReentrantLock lockPool = new ReentrantLock();
	protected static final ReentrantLock REENTRANT_LOCK = new ReentrantLock();

	private static Logger _log = LoggerFactory.getLogger(RedisUtil.class);
	// 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
	private static boolean TEST_ON_BORROW = false;

	private static volatile JedisPool jedisPool = null;

	/**
	 * redis过期时间,以秒为单位
	 */
	public final static int EXRP_HOUR = 60 * 60; // 一小时
	public final static int EXRP_DAY = 60 * 60 * 24; // 一天
	public final static int EXRP_MONTH = 60 * 60 * 24 * 30; // 一个月

	static {
		try {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(RedisConfig.MAX_ACTIVE);
			config.setMaxIdle(RedisConfig.MAX_IDLE);
			config.setMaxWaitMillis(RedisConfig.MAX_WAIT);
			config.setTestOnBorrow(TEST_ON_BORROW);
			jedisPool = new JedisPool(config, RedisConfig.IP, RedisConfig.PORT, RedisConfig.TIMEOUT);
		} catch (Exception e) {
			_log.error("First create JedisPool error : " + e);
		}
	}

	/**
	 * 初始化Redis连接池
	 */
	private static void initialPool() {
		if (jedisPool != null) {
			try {
				JedisPoolConfig config = new JedisPoolConfig();
				config.setMaxTotal(RedisConfig.MAX_ACTIVE);
				config.setMaxIdle(RedisConfig.MAX_IDLE);
				config.setMaxWaitMillis(RedisConfig.MAX_WAIT);
				config.setTestOnBorrow(TEST_ON_BORROW);
				jedisPool = new JedisPool(config, RedisConfig.IP, RedisConfig.PORT, RedisConfig.TIMEOUT);
			} catch (Exception e) {
				_log.error("First create JedisPool error : " + e);
			}
		}
	}

	/**
	 * 在多线程环境同步初始化
	 */
	private static synchronized void poolInit() {
		if (null == jedisPool) {
			initialPool();
		}
	}

	/**
	 * 同步获取Jedis实例
	 *
	 * @return Jedis
	 */
	public synchronized static Jedis getJedis() {
		poolInit();
		Jedis jedis = null;
		try {
			if (null != jedisPool) {
				jedis = jedisPool.getResource();
				if (RedisConfig.PASSWORD != null) {
					jedis.auth(RedisConfig.PASSWORD);
				}
				// 获取jedis的时候设置dbindex add by liux 20191211
				jedis.select(RedisConfig.JEDIS_DB_INDEX);
			}
		} catch (Exception e) {
			_log.error("Get jedis error : " + e);
			_log.error(" ip-> " + RedisConfig.IP + " port-> " + RedisConfig.PORT);
		}
		return jedis;
	}

	/**
	 * 设置 String
	 *
	 * @param key
	 * @param value
	 * @param dbSelect
	 *            (设置DB库,传null默认为db0)
	 */
	public synchronized static void set(String key, String value, Integer dbSelect) {
		Jedis jedis = null;
		try {
			value = StringUtils.isBlank(value) ? "" : value;
			jedis = getJedis();
			jedis.select(dbSelect == null ? RedisConfig.JEDIS_DB_INDEX : dbSelect);
			jedis.set(key, value);
			jedis.expire(key, EXRP_MONTH);
		} catch (Exception e) {
			_log.error("Set key error : " + e);
		} finally {
			if (null != jedis) {
				jedis.close();
			}
		}
	}
}

 RedisConfig和RedisUtil均在上图的common模块,假如car模块需要用到redis只需要依赖一下common并在配置文件中配置上redis的配置即刻使用,但是cec模块同样依赖了common模块,并且它不需要使用redis,由于RedisConfig中使用了@Configuration注解,当cec模块依赖了common但是未配置redis的配置,启动时就会报错找不到配置文件;

不管用不用这个注解或者其他的注解方式读取配置文件,均不能满足上述需求;

分析:这个redis的配置文件,应该是在我业务中需要使用时才去读取;

猜想:传统读取配置文件时使用到了java.util包下面的ResourceBundle进行读取过.properties后缀的配置文件,猜想是否能用同样的方式来读取.yml配置文件呢?

我的配置文件路径如下图(注:多了一个config,故代码读取也需要加一层):

 

尝试:

1.先读取环境配置spring.profiles.active;

2.根据环境配置拼接实际环境的配置文件;

/**
     * @Description: 根据环境区分,读取对应环境配置文件配置(yml格式)
     * @Author: Niel
     * @Date: 2021/9/9 8:48 上午
     * @params:
     * @param propertyFileName yml配置文件名,不需要后缀
     * @param propertyName 属性名称
     * @return: java.lang.String
     **/
    public static String getYmlStringForActive(String propertyFileName, String propertyName) {
        YamlPropertiesFactoryBean yamlMapFactoryBean = new YamlPropertiesFactoryBean();
        yamlMapFactoryBean.setResources(new ClassPathResource("config/application.yml"));
        Properties properties = yamlMapFactoryBean.getObject();
        String active = properties.getProperty("spring.profiles.active");
        yamlMapFactoryBean = new YamlPropertiesFactoryBean();
        yamlMapFactoryBean.setResources(new ClassPathResource("config/"+propertyFileName + "-" + active + ".yml"));
        properties = yamlMapFactoryBean.getObject();
        //获取yml里的参数
        String param = properties.getProperty(propertyName);
        if(StringUtil.isBlank(param)){
            return "0";
        }
        return param;
    }

运行car模块进行读取:

public class AcigaChargeMotorServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(AcigaChargeMotorServiceApplication.class, args);
        String redisIp = ResourceBundleUtil.getYmlStringForActive("application","redis.ip");
        System.out.println("==================测试读取yml文件"+redisIp);
    }
}

结果:

 完美读取到配置文件,再次启动cec模块也不会报错,因为业务中不需要调用它,自然不会进行读取,也不会报错;

已达到目的,尝试部署测试环境,进行验证;

结果悲剧,发现仍然读取的还是dev的开发环境配置;

原因:

nohup java -jar -Dspring.profiles.active=test

启动脚本中指定的环境为test,但是实际application配置文件中的spring.profiles.active还是dev

观察springboot启动时发现有一行输出:

2021-09-15 15:25:24.530 1 main INFO c.a.c.m.a:655 - The following profiles are active: dev

猜想:这里能打印出dev来,我们是否能同样使用静态的方式得到这个参数? 

 (1)跟踪代码:SpringApplication.run方法

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            analyzers = new FailureAnalyzers(context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);  // 在这里打印了,跟踪进去
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

 (2)跟踪代码:SpringApplication.prepareContext方法

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   applyInitializers(context);
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);  // 名称很明显,继续跟踪进去
   }
   ......
}

(3)跟踪代码:SpringApplication.logStartupProfileInfo方法

protected void logStartupProfileInfo(ConfigurableApplicationContext context) { 
   Log log = getApplicationLog();
   if (log.isInfoEnabled()) {
      String[] activeProfiles = context.getEnvironment().getActiveProfiles();
      if (ObjectUtils.isEmpty(activeProfiles)) {
         String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
         log.info("No active profile set, falling back to default profiles: "
               + StringUtils.arrayToCommaDelimitedString(defaultProfiles)); 
      }
      else {
         log.info("The following profiles are active: "
               + StringUtils.arrayToCommaDelimitedString(activeProfiles));  //找到了,很明显用了ApplicationContxt容器,接下来就是写个工具类来获取Application就行啦。
 
     }
   }
}

正好工具类中有一个SpringContextUtil:

package com.xx.xx.common.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @ClassName SpringContextUtil
 * @ProjectName aciga-charge
 * 通过spring的bean的名称,获取spring中的容器对象
 * @author Niel
 * @date 2021/7/19 3:19 下午
 * @Version 1.0
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {

	private static ApplicationContext context = null;

	private SpringContextUtil() {
		super();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		context = applicationContext;
	}

	/**
	 * 根据名称获取bean
	 * @param beanName
	 * @return
	 */
	public static Object getBean(String beanName) {
		return context.getBean(beanName);
	}

	/**
	 * 根据bean名称获取指定类型bean
	 * @param beanName bean名称
	 * @param clazz 返回的bean类型,若类型不匹配,将抛出异常
	 */
	public static <T> T getBean(String beanName, Class<T> clazz) {
		return context.getBean(beanName, clazz);
	}
	
.......

	/// 获取当前环境
	public static String getActiveProfile() {
		return context.getEnvironment().getActiveProfiles()[0];
	}

}

改写读取配置文件方法:

/**
     * @Description: 根据环境区分,读取对应环境配置文件配置(yml格式)
     * @Author: Niel
     * @Date: 2021/9/9 8:48 上午
     * @params:
     * @param propertyFileName yml配置文件名,不需要后缀
     * @param propertyName 属性名称
     * @return: java.lang.String
     **/
    public static String getYmlStringForActive(String propertyFileName, String propertyName) {
        YamlPropertiesFactoryBean yamlMapFactoryBean = new YamlPropertiesFactoryBean();
        yamlMapFactoryBean.setResources(new ClassPathResource("config/application.yml"));
        Properties properties = yamlMapFactoryBean.getObject();
        String active = SpringContextUtil.getActiveProfile();//读取当前环境
        yamlMapFactoryBean = new YamlPropertiesFactoryBean();
        yamlMapFactoryBean.setResources(new ClassPathResource("config/"+propertyFileName + "-" + active + ".yml"));
        properties = yamlMapFactoryBean.getObject();
        //获取yml里的参数
        String param = properties.getProperty(propertyName);
        if(StringUtil.isBlank(param)){
            return "0";
        }
        return param;
    }

再次测试,完美解决,结果我就不贴出来了;

至此静态方法读取yml配置文件搞定

【AI编程】Qoder AI 编程工具从部署到深度使用实战详解 Qoder AI 编程工具从部署到深度使用实战详解 阅读详情

相关推荐

吃透 AM32 无人机电调:从源码架构到工作原理的全方位解析(附实践指南)(上)

本文深入解析了AM32电调固件的源码架构与核心模块实现。文章首先介绍了AM32电调作为开源无人机;动力心脏的重要地位,随后从硬件基础、源码目录、模块划分三个维度构建解析框架。重点剖析了系统初始化模块的时钟配置、GPIO初始化和定时器设置,以及FOC控制模块的坐标变换算法、角度估算和SVPWM生成等关键技术实现。通过逐行代码解析,揭示了电调如何将飞控指令转换为电机驱动的完整过程,为开发者理解AM32电调工作原理提供了系统性指导。

bother3000的博客 6577

Spring Boot读取Yml配置文件的3种方法

Spring Boot读取Yml配置文件

zlfjavahome的专栏 1万+

GOM Correlate拉伸试验后处理操作

本文描述了使用GOM Software 2022对拉伸试样进行后处理的操作,包括设置检测点、引伸计、输出应变数据到表格、输出报告为视频等

qq_45008924的博客 4668

Spring Boot中静态方法读取yml配置文件的办法。

yml文件 一、Spring Boot读取yml文件的几种方式 1、使用@Value注解 2、使用@ConfigurationProperties 3、测试 在测试类中注入 @Autowired private Info info; 这里可以看到已经读取到yml中的文件。 二、在静态方法中读取yml中配置的属性 直接说明结果,当我们在静态方法中读取的时候,会发现读取到的Info为null...

qq_17769897的博客 1万+

STM32平台下FreeModbus TCP协议栈集成指南

深入讲解在STM32平台如何移植和配置freemodbus协议栈,实现TCP通信功能。结合嵌入式开发细节,帮助开发者快速掌握freemodbus的应用与调试技巧。

weixin_36001279的博客 1144

深度探索:非线性随机森林(Nonlinear Random Forest,NL-RF)原理及应用

非线性随机森林(NL-RF)旨在通过结合集成方法与高级技术来捕捉和建模数据中的非线性关系,从而改善传统随机森林在处理复杂非线性数据结构时的表现。

qq_51320133的博客 2068

springboot读取yml配置绑定静态属性

springboot读取yml配置绑定静态属性 1、yml 文件配置 wechat: appid: xxxxxxxxxxx 2、启动类添加注解@EnableConfigurationProperties @EnableConfigurationProperties({ WechatConfig.class }) public class Application { public st...

1853 2392

5、癌症数据预处理技术与深度学习在癌症预测中的应用

本文详细介绍了癌症数据预处理技术和深度学习在癌症预测中的应用。针对CT扫描图像、X射线图像、超声图像、基因表达数据和文本数据等不同类型的癌症数据,分别阐述了去噪、插值、配准、归一化等预处理技术的具体方法。同时,分析了深度学习在乳腺癌、白血病和宫颈癌预测中的作用,结合相关影响因素探讨了模型的应用潜力。此外,还总结了深度学习在癌症预测中面临的挑战及未来发展方向,为癌症的早期诊断与精准预测提供了理论支持和技术参考。

alice7model的博客 123

springboot静态代码块中读取配置文件yml

1、配置文件application.yml mqtt: client: username: admin password: public 2、 @Configuration public class MqttConfig { ​ private static String username; ​ private static String password; @Value("${mqtt.client.username}") publ...

qq_41547474的博客 2549

高通平台开发系列讲解(USB篇)MBIM协议详解

📢本文主要介绍高通平台USB网卡MBIM协议。

内核笔记 5440

分类预测 | MATLAB实现ELM极限学习机多特征分类预测(二分类)

分类预测 | MATLAB实现ELM极限学习机多特征分类预测(二分类)

机器学习之心的博客,关注并私信文章链接,获取对应文章源码和数据。 781

springboot不同环境加载不同application.yml配置文件

springboot不同环境加载不同application.yml配置文件,你还不会吗?

IT_Most的博客 4667

Django 6.0:有哪些新特性以及它们对实际项目的重要性

Django 6.0 感觉像是一个成熟的版本,它基于实际应用案例而打造。它减少了不必要的复杂性,同时扩展了框架的开箱即用功能。升级到 Django 6.0 不仅仅是为了保持版本更新,更是为了构建更易于维护、更安全、更能适应未来发展的应用程序。

iCloudEnd的博客 341

不同环境_springboot不同环境加载不同application.yml配置文件

平凡也就两个字: 懒和惰;成功也就两个字: 苦和勤;优秀也就两个字: 你和我。跟着我从0学习JAVA、spring全家桶和linux运维等知识,带你从懵懂少年走向人生巅峰,迎娶白富美!每一篇文章都是心得总结,跟我学习你就是大牛!springboot不同环境加载不同application.yml配置文件1 背景 日常工作中常常有不同的环境,如:开发环境(sit)、测试环境(uat...

weixin_36259939的博客 1944

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

【SpringBoot系列】读取yml文件的几种方式

Spring Boot读取yml文件的主要方式有以下几种:​ 我们可以在bean的属性上使用@Value注解,直接读取yml中的值,如:application.yml: Bean: 2.Environment对象 我们可以通过注入Environment对象来读取yml值,如: 3.@ConfigurationProperties注解 我们可以使用@ConfigurationProperties注解将yml中的值映射到bean的属性上,如:application.yml: Bean: 4.YmlProper

Venus's Blog 9万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

springboot 获取yml变量_springboot怎么读取不同yml配置文件

我配置了三个文件夹,dev、prod、test 代表三个不同的环境配置文件,然后通过最外层的application.yml来加载不同文件夹的配置文件,其中system.yml的配置为:system:config: "dev-env"application-dev.yml为:#启动端口配置server:port: 8085#spring配置spring:application:name: multi...

weixin_39704314的博客 611

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

一文带你实现SpringBoot多配置文件实现环境动态切换(prod、dev、test)

在日常开发中,我们可能经常会碰到要调试代码或者切换到线上环境去查看问题。 在没有配置环境切换的情况下,我们需要来回修改各种配置地址,例如 MySQL、Redis。。。等等 所以,当你看完本文,你可以学到如何对一个SpringBoot单体项目进行改造,实现dev、prod、test环境的动态切换。

weixin_44048876的博客 8070

SpringBoot中如何动态获取yml配置文件?

SpringBoot中如何动态获取yml配置文件? 文章目录SpringBoot中如何动态获取yml配置文件?一、YAML基本语法二、YAML支持的数据格式1.字面量:数字、字符串、布尔等不可再分的值2.对象:即为键值对,key= value3.数组:一组按顺序排列的值三、读取yml配置文件1.新建配置文件2.添加配置文件对应的实体类3.添加controller四、测试五、总结 一、YAML基本语法 以缩进代表层级关系; 缩进不能使用tab,只能用空格; 空格个数不重要,但是同一层级必须左对齐; 大小

子衿的博客 4627

SpringBoot中,logback.xml里面读取yml里面的参数方式

文章目录解决方法步骤一:使用logback-spring.xml步骤二:xml中增加springProperty 前应提要:使用logback做日志,日志的输出位置可能要根据部署的环境动态进行配置,而logbackxm.xml中使用${xxx.xxx.xxx}读取不到系统参数 解决方法 步骤一:使用logback-spring.xml 将原先的logback.xml改成logback-spri...

Doubletree.lin的博客 1万+

springboot xml文件读取yml文件配置信息

springboot xml文件动态读取yml文件配置信息,在不同环境下读取对应配置文件的参数信息 1.借助springboot springProperty的标签实现 2.yml文件配置参数 graylog: url: XXXXX3.logback.xml 文件读取参数 <configuration>//配置参数 <springProperty scope...

weixin_30918415的博客 3023

springboot项目配置文件(*.yml、*.properties),使用用环境变量加载配置动态值

如需转载分享,请标明出处,且不用于盈利为目的,谢谢合作! springboot项目配置文件(.yml、.properties),使用用环境变量加载配置动态值

এ某年某月某日এ的博客 4068

Spring Boot 核心配置文件,yml 语法规范、多环境配置、配置文件加载顺序,EnvironmentAware 读取 yml 配置

目录 需求分析 多 profile 文件形式 yml 文档块形式 需求分析 Profile 是 Spring 对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境 项目开发时有开发环境、测试环境、部署环境等,可以通过 profile 配置切换 多 profile 文件形式 格式:application-{profile}.properties/yml,如:ap...

蚩尤后裔-汪茂雄 1万+

springcloud+springboot框架 动态加载配置文件 bootstrap.yml/properties文件

1、springboot动态指定对应环境的yml文件 1.1 SpringBoot在启动加载配置文件时,如没有明确指定spring.profiles.active属性,默认是加载application.yml或application.properties文件。 还可以使用以下命名约定来定义特定于配置文件的属性: application-sit.yml或application-sit.properties;application-xxx.yml或application-xxx.properties等等 2、s

qq_40448069的博客 7693

记 springboot修改配置文件(properties或yml)

#记 springboot修改配置文件(properties或yml) 最近项目开发快完成了(ssm—&gt;springboot架构),在项目构建过程中有很多数据都是配置在properties或yml文件中,昨天经理给我提了个需求:写个api去修改配置文件中的数据值,然后还要用日志记录. 当然作为一个java程序员(面向百度程序员…呸! 面向对象…)本着学习的精神去搜索了一波大神们的操作,结果发...

recover_v的博客 1万+

【SpringBoot 配置文件】properties 与 yml 的基础用法、格式及优缺点

本文围绕 Spring Boot 配置文件展开,先介绍 properties 与 yml 两种主流格式;接着讲解 properties 的读取方法及局限性;随后深入 yml 的语法规则,详解其数据类型、对象、集合等配置与读取实操,最后对比两者优缺点。全文系统梳理核心知识,助力读者快速掌握 Spring Boot 配置技能。

独断万古他化的博客 1163

SpringBoot使用注解(@value)动态读取properties(yml)文件中配置信息

为了简化读取properties文件中的配置值,spring支持@value注解的方式来获取,这种方式大大简化了项目配置,提高业务中的灵活性。 两种使用方法 1)@Value("#{configProperties[‘key’]}") 2)@Value("${key}") 配置文件示例 ftp: ftplp: 10.2.23.89 ftpPort: 21 ftpUser: uftp ftp...

大鱼的博客 9777
上一篇: 微信公众号支付报错:当前的url未注册解决方法(百分百解决不含糊)
下一篇: OCPP 1.6 接入实现文档
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值