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

相关文章

Java注解之超越Javadoc的元数据利器详解

《Java注解之超越Javadoc的元数据利器详解》本文将深入探讨Java注解的定义、类型、内置注解、自定义注解、保留策略、实际应用场景及最佳实践,无论是初学者还是资深开发者,都能通过本文了解如何利用... 目录什么是注解?注解的类型内置注编程解自定义注解注解的保留策略实际用例最佳实践总结在 Java 编程

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

《SpringBoot整合mybatisPlus实现批量插入并获取ID详解》这篇文章主要为大家详细介绍了SpringBoot如何整合mybatisPlus实现批量插入并获取ID,文中的示例代码讲解详细... 目录【1】saveBATch(一万条数据总耗时:2478ms)【2】集合方式foreach(一万条数

SpringBoot中配置文件的加载顺序解读

《SpringBoot中配置文件的加载顺序解读》:本文主要介绍SpringBoot中配置文件的加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot配置文件的加载顺序1、命令⾏参数2、Java系统属性3、操作系统环境变量5、项目【外部】的ap

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

SpringBoot UserAgentUtils获取用户浏览器的用法

《SpringBootUserAgentUtils获取用户浏览器的用法》UserAgentUtils是于处理用户代理(User-Agent)字符串的工具类,一般用于解析和处理浏览器、操作系统以及设备... 目录介绍效果图依赖封装客户端工具封装IP工具实体类获取设备信息入库介绍UserAgentUtils