bean中字段获取配置文件中的数据

2024-03-01 19:08

本文主要是介绍bean中字段获取配置文件中的数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如果想要将配置文件中的配置信息赋于bean中字段可以使用@Value和@ConfigurationProperties这两种形式。在@Value中一般使用的是@Value(“${xxx.yyy.zzz:defaultValue}”)这种形式,我们可以获取配置文件中key对应的值,如果找不到该配置则使用后面跟的那个默认值。

例如配置文件application.properties中有
spring.application.name=myProject
feign.hystrix.enabled=true可以使用该种形式获取对应的配置key对应的value值 
@Value("${spring.application.name}”)
String applicationName; 

@Value的实现方式主要与org.springframework.core.env.PropertySource 、org.springframework.core.env.Environment 有关。PropertySource可以理解为数据源,它其中有个方法叫做getProperty(String name),通过key的名字获取数据源中对应的key-value对的value值,我们的配置文件就是一个数据源。
Environment 即项目的环境配置信息它其中有两个方法,String resolvePlaceholders(String text) 解析@Value("${xxxx}")xxxx,

ConfigurableEnvironment 中包含MutablePropertySources getPropertySources()获取所有的PropertySource实例,PS:MutablePropertySource实现了PropertySource接口。那么从上我们就可以大概推出@Value的执行过程
(1)environment使用resolvePlaceholders解析@Value中的配置key
(2)environment使用getPropertysources方法获取MutablePropertySources,它其中有一个包含所有的PropertySource的list
(3)利用MutablePropertySources对所有的PropertySource进行遍历,使用getProperty查询对应的value

那么除了将配置信息放在配置文件中,从上面我们看出来其实是从propertySource中获取key对应的value,所有我们还可以将配置信息放在数据库或其他存储媒介中,只是在项目初始化时,需要将对应的配置信息生成一个propertySource对象,放入MutablePropertySources中,简单给个代码。

/*** 邮件配置信息*/
@Data
@Component
public class MailConfig {@Value("${mail.host}")private String host;@Value("${mail.username}")private String username;@Value("${mail.password}")private String password;
}@Test
public void test2() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();//模拟从db中获取配置信息Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类)MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);//将mailPropertySource丢在Environment中的PropertySource列表的第一个中,让优先级最高context.getEnvironment().getPropertySources().addFirst(mailPropertySource);/*上面这段是关键 end*/context.register(MainConfig2.class);context.refresh();MailConfig mailConfig = context.getBean(MailConfig.class);System.out.println(mailConfig);
}

一般情况下我们是使用@Value(“${xxx.yyy}”)去获取对应配置文件中的value,其实还有一种形式是@Value(“#{}”),那么这个#标记的@Value是干嘛的呢?#表示执行一个spel表达式
(1) 获取常量 @Value(“#{1}”)、 @Value(“#{‘字符串常量’}”)
(2) 获取bean的属性,但是要确保被获取属性的bean和本bean在同一个容器中,如@Value(“#{aBean.name}”),获取aBean的name属性
(3) 调用bean的方法,如@Value(“#{aBean.getName}”),获取aBean的getName方法返回值下面再简单说一下@ConfigurationProperties,它可以利用配置前缀获取配置中的配置信息,一般用于配置类之上,举个简单的小例子。以七牛公共和私有bucket配置为例

bootstrap.yml中有
qiniu:buckets:private:bucket: yqg-private-secretdomain: http://private-domain/public:bucket: yqg-public-testdomain: https://public-domain/那么其对应的配置类格式为,可以看到我们甚至可以根据配置文件中配置属性的层级去定义配置类的结构
@Data
@ConfigurationProperties(prefix = "qiniu")
public class QiniuProperties {private Map<String, BucketConfig> buckets = new HashMap<>();@Datapublic static class BucketConfig {private String bucket;private String domain;}
}

由此我们可以看出一些@Value和@ConfigurationProperties的区别,如果仅仅想获取其中一个配置的value那么就用@value,如果想要获取一类配置的value,那么可以使用@ConfigurationProperties,它一个用在属性字段上,一个用在类上,且@Value支持$和#两种形式,#可以获取常量、其他bean的属性字段,方法返回值。

@Value和@ConfigurationProperties动态刷新

现在大家的项目中一般都会使用配置中心用来动态获取配置信息,不管是Spring-boot的配置管理、zookeeper还是携程的apollo都为我们提供了便捷的配置管理功能。当配置中心的配置发生改变时,会通知客户端服务,更改environment中的对应的配置信息,当environment发生更改时会触发EnvironmentChangeEvent事件,其中包括了keys字段,就是发生变更的配置名。
@ConfigurationProperties原本就支持配置自动刷新,在ConfigurationPropertiesRebinder中会监听EnvironmentChangeEvent事件,将所有被@ConfigurationProperties注解修饰的bean进行重新绑定,此时就会刷新@ConfigurationProperties标识bean中的属性值。而@Value注解标识的字段并不会自动刷新,需要搭配@RefreshScope进行刷新。

如果我们使用了@Value,创建bean时获取的配置value值会赋值给在bean中对应属性字段,它只会在bean创建的过程中获取一次,即使以后通过配置中心改变了environment中key对应的value,@Value修饰字段的值也不会改变。那么该如何对它们进行动态刷新呢?
       在spring-cloud中提供了一个注解叫做@RefreshScope,它可以提供配置的动态刷新。先进到@RefreshScope这个注解中去看一下,注解的内容很简单就是把使用了该注解的类,它的@Scope作用域置为fresh,ScopeProxyMode使用TARGET_CLASS,看一下它的英文注释,大概就是说使用该注解修饰的bean可以在运行时刷新,每次使用bean时都会创建一个新的bean,而ScopedProxyMode.TARGET_CLASS表示创建该bean时实际返回的通过cglib产生的一个代理对象。

/*** Convenience annotation to put a <code>@Bean</code> definition in* {@link org.springframework.cloud.context.scope.refresh.RefreshScope refresh scope}.* Beans annotated this way can be refreshed at runtime and any components that are using* them will get a new instance on the next method call, fully initialized and injected* with all dependencies.* * @author Dave Syer**/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {/*** @see Scope#proxyMode()*/ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}

它的实现类RefreshScope.java->GenericScope->Scope(接口),可以看一下它的类注释,我就不贴了,其中写了被@RefreshScope修饰的bean会被置为懒加载,只有在第一次使用它的时候才会进行创建,如果有其他的bean依赖与它,那么它获取的只是被@RefreshScope修饰bean的一个代理。
(1)当第一次使用时,会将对应bean创建并生成代理,然后放入到一个cache之中,当不刷新时,获取的代理对象都是这一个
(2)当属性发生改变,environment中对应的value发送改变,然后会发出refresh请求,会调用RefreshScope.refreshAll()或refresh(string name)方法,前者为刷新全部配置,后者为刷新指定bean的配置,执行GenericScope的destory()或destory(name)方法,并发送refreshScope事件,供监听处理。
(3)在GenericScope的destory方法中会将cache中全部或指定name的代理对象缓存删除,然后对应代理对象销毁
(4)当下次在使用对象中的属性或方法时会调用GenericScope的get方法重新利用新的配置value值生成代理对象,并放入缓存

下面简单给个代码

//bootstrap.yml
mail:username: myUserName//配置类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;@Component
@RefreshScope
public class TestRefreshScope {@Value("${mail.username}")private String name;public String getName() {return name;}
}//直接在idea里建了个eureka项目,在它启动类里写的测试代码
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(EurekaServerApplication.class, args);System.out.println("在修改配置值之前");for (int i=1; i<=3; i++) {TestRefreshScope obejct = context.getBean(TestRefreshScope.class);System.out.println(obejct);System.out.println(obejct.getName());System.out.println("================================");}System.out.println("刷新后");//模拟自定义propertySource并刷新现有的邮箱配置,它可以来自数据库等其他存储中介for (int i=1; i<=3; i++) {refreshMailPropertySource(context);RefreshScope refreshScope = context.getBean(RefreshScope.class);//手动触发refresh,正常应该是执行一个refresh post请求refreshScope.refresh("testRefreshScope");TestRefreshScope obejct = context.getBean(TestRefreshScope.class);System.out.println(obejct);System.out.println(obejct.getName());System.out.println("================================");}}private static void refreshMailPropertySource(ConfigurableApplicationContext context) {Map<String, Object> mailInfo = new HashMap<>();String uuid = UUID.randomUUID().toString();System.out.println("新生成的uuid="+uuid);mailInfo.put("mail.username", uuid);//将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类)MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfo);context.getEnvironment().getPropertySources().addFirst(mailPropertySource);}
}

执行结果如下

在修改配置值之前
com.springboot.eurekaserver.TestRefreshScope@6546371
myUserName
================================
com.springboot.eurekaserver.TestRefreshScope@6546371
myUserName
================================
com.springboot.eurekaserver.TestRefreshScope@6546371
myUserName
================================
刷新后
新生成的uuid=f48dd171-440b-48cd-b5be-0e3ebd6d2668
com.springboot.eurekaserver.TestRefreshScope@227a933d
f48dd171-440b-48cd-b5be-0e3ebd6d2668
================================
新生成的uuid=c13c562c-5d5f-4607-ac5b-46356fb448a1
com.springboot.eurekaserver.TestRefreshScope@7bc2ae16
c13c562c-5d5f-4607-ac5b-46356fb448a1
================================
新生成的uuid=d49de0bf-11eb-4589-8eb7-f6d5ec0022ba
com.springboot.eurekaserver.TestRefreshScope@64910b2d
d49de0bf-11eb-4589-8eb7-f6d5ec0022ba
================================

这篇关于bean中字段获取配置文件中的数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763508

相关文章

5分钟获取deepseek api并搭建简易问答应用

《5分钟获取deepseekapi并搭建简易问答应用》本文主要介绍了5分钟获取deepseekapi并搭建简易问答应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1、获取api2、获取base_url和chat_model3、配置模型参数方法一:终端中临时将加

解决Spring运行时报错:Consider defining a bean of type ‘xxx.xxx.xxx.Xxx‘ in your configuration

《解决Spring运行时报错:Considerdefiningabeanoftype‘xxx.xxx.xxx.Xxx‘inyourconfiguration》该文章主要讲述了在使用S... 目录问题分析解决方案总结问题Description:Parameter 0 of constructor in x

Java中注解与元数据示例详解

《Java中注解与元数据示例详解》Java注解和元数据是编程中重要的概念,用于描述程序元素的属性和用途,:本文主要介绍Java中注解与元数据的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参... 目录一、引言二、元数据的概念2.1 定义2.2 作用三、Java 注解的基础3.1 注解的定义3.2 内

将sqlserver数据迁移到mysql的详细步骤记录

《将sqlserver数据迁移到mysql的详细步骤记录》:本文主要介绍将SQLServer数据迁移到MySQL的步骤,包括导出数据、转换数据格式和导入数据,通过示例和工具说明,帮助大家顺利完成... 目录前言一、导出SQL Server 数据二、转换数据格式为mysql兼容格式三、导入数据到MySQL数据

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

一文详解Python中数据清洗与处理的常用方法

《一文详解Python中数据清洗与处理的常用方法》在数据处理与分析过程中,缺失值、重复值、异常值等问题是常见的挑战,本文总结了多种数据清洗与处理方法,文中的示例代码简洁易懂,有需要的小伙伴可以参考下... 目录缺失值处理重复值处理异常值处理数据类型转换文本清洗数据分组统计数据分箱数据标准化在数据处理与分析过

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

C#实现系统信息监控与获取功能

《C#实现系统信息监控与获取功能》在C#开发的众多应用场景中,获取系统信息以及监控用户操作有着广泛的用途,比如在系统性能优化工具中,需要实时读取CPU、GPU资源信息,本文将详细介绍如何使用C#来实现... 目录前言一、C# 监控键盘1. 原理与实现思路2. 代码实现二、读取 CPU、GPU 资源信息1.

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处