- 1. @ConfigurationProperties
- —————————————————————普通属性测试
- —————————————————————简单集合与数组测试
- 等价于
- myListOrArray: [d,e,f] myListOrArray: d,e,f
- 二维数组
- myListOrArray: [[d],[e],[f]]
- myListOrArray:
- -
- - d
- - e
- - f
- -
- - c
- —————————————————————简单Map测试
- 等价于
- myMap: {k4: v4,k5: v5,k6: v6}
- 等价于
- myMap[k1]: v1
- myMap[k2]: v2
- myMap[k3]: v3
- —————————————————————对象测试
- 等价于
- myUser: {name: 王五,age: 50,gender: 男,motto: 好好1}
- —————————————————————复杂集合与数组测试
- 等价于
- myListOrArrayPlus:
- - {name: 王五,age: 50,gender: 男,motto: 好好2}
- - {name: 王五1,age: 50,gender: 男,motto: 好好2}
- 等价于
- myListOrArrayPlus:
- -
- name: 王五2
- —————————————————————复杂Map测试
- myMapPlus: {k1: {name: 王五,age: 50,gender: 男,motto: 好好1},k2: {name: 王五1,age: 50,gender: 男,motto: 好好1}}
1. @ConfigurationProperties
@ConfigurationProperties是springboot提供读取配置文件的一个注解。其对应的bean的后置处理器为ConfigurationPropertiesBindingPostProcessor
public class ConfigurationPropertiesBindingPostProcessor
implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {
它是实现了BeanPostProcessor接口,在bean被实例化后,会调用后置处理,递归的查找属性,通过反射注入值,对大多数属性而言强制需提供其setter和getter方法。
但是属性名称不要求一定相同,只需保证配置文件的属性和setter方法名相同即可。
不过@ConfigurationProperties注解,只可以把应用主配置文件的内容读取进来,要想给Bean赋值,只能读取主配置文件,就是application.properties或者application.yml文件,若我们想自己写一个配置文件来给bean赋值,我们就可以使用该注解@PropertyResource
我们在resource里边新建一个student.properties文件,并把之前主配置文件里的内容剪贴进来,并在bean上边加上注解
@Component
@ConfigurationProperties(prefix = "student")
@PropertyResource(value = {"classpath:student.properties"})
// value可以添加多个配置文件,classpath指的是类路径,表示类路径下的student.properties文件
public class Student {
......
}
1.@ConfigurationProperties读取配置文件,注入bean的三种方式
1. 配合@Component注解直接进行注入(主要)
@Component
@ConfigurationProperties(prefix = "student")
public class Student {
......
}
2. 使用@EnableConfigurationProperties,通常配置在标有@Configuration的类上,当然其他@Component注解的派生类也可以,不过不推荐.(摘取)(springboot源码大量使用,例如DataSourceAutoConfiguration,FreeMarkerAutoConfiguration)
@ConfigurationProperties(prefix = "doc")
public class DocumentServerProperties {
//代码...
}
//@EnableConfigurationProperties的作用:使@ConfigurationProperties注解的类生效,并将标注@ConfigurationProperties注解的类注册到spring容器中
//@EnableConfigurationProperties({DocumentServerProperties.class}) 只是让DocumentServerProperties这个类生效,并将其注册到spring容器中
@EnableConfigurationProperties
@Configuration
public class SomeConfiguration {
}
3. 使用@Bean方式在标有@Configuration的类进行注入,这种方式通常可以用在对第三方类进行配置属性注册(摘取)
public class ThirdComponent {
...
}
@Configuration
public class SomeConfiguration {
@ConfigurationProperties("demo")
@Bean
public ThirdComponent thirdComponent(){
return new ThirdComponent();
}
}
2. @ConfigurationProperties(yml/properties) 和 @Value(“xx”) 区别
3. @ConfigurationProperties绑定配置信息至Array、List、Map、Bean的实现
如果application.properties与自定义的 properties有重复的属性,取application.properties配置的属性值
@Data
public class User {
private String name;
private Integer age;
private String gender;
private String motto;
}
@Component
@ConfigurationProperties(prefix = "prop")
@Data
public class DemoConfig {
private String normalString;
private Integer normalNumber;
private Boolean normalBoolean;
/**
* 类型为数组或者集合都可以
*/
private String[] myListOrArray;
private Map<String, Object> myMap;
private User myUser;
/**
* 类型为数组或者集合都可以
*/
private List<User> myListOrArrayPlus;
private Map<String, User> myMapPlus;
}
1. application.properties
#------------------------------------------普通属性测试
prop.normalString = value1
prop.normalNumber = 123
prop.normalBoolean = false
#------------------------------------------简单集合与数组测试
prop.myListOrArray[0] = a
prop.myListOrArray[1] = b
prop.myListOrArray[2] = c
# 等价于
# prop.myListOrArray = a,b,c
#------------------------------------------简单Map测试
prop.myMap[k1] = v1
prop.myMap[k2] = v2
prop.myMap[k3] = v3
# 等价于
# prop.myMap.k1 = v1
# prop.myMap.k1 = v1
# prop.myMap.k1 = v1
#------------------------------------------对象测试
prop.myUser.name = 张三
prop.myUser.[age] = 28
prop.myUser.gender = 男
prop.myUser.motto = 岁月
# 等价于
# prop.myUser[name] = 张三
# prop.myUser[age] = 28
# prop.myUser[gender] = 男
# prop.myUser[motto] = 岁月静好
#------------------------------------------复杂集合与数组测试
prop.myListOrArrayPlus[0].name = 张三
prop.myListOrArrayPlus[0].age = 28
prop.myListOrArrayPlus[0].gender = 男
prop.myListOrArrayPlus[0].motto = 岁月
prop.myListOrArrayPlus[1].name = 李四
prop.myListOrArrayPlus[1].age = 28
prop.myListOrArrayPlus[1].gender = 女
prop.myListOrArrayPlus[1].motto = 年华
# 等价于
# prop.myListOrArrayPlus[0][name] = 张三
# prop.myListOrArrayPlus[0][age] = 28
# prop.myListOrArrayPlus[0][gender] = 男
# prop.myListOrArrayPlus[0][motto] = 岁月
# prop.myListOrArrayPlus[1][name] = 李四
# prop.myListOrArrayPlus[1][age] = 28
# prop.myListOrArrayPlus[1][gender] = 女
# prop.myListOrArrayPlus[1][motto] = 年华
#------------------------------------------复杂Map测试
prop.myMapPlus[k1].name = 张三
prop.myMapPlus[k1].age = 28
prop.myMapPlus[k1].gender = 男
prop.myMapPlus[k1].motto = 岁月
prop.myMapPlus[k2].name = 李四
prop.myMapPlus[k2].age = 28
prop.myMapPlus[k2].gender = 女
prop.myMapPlus[k2].motto = 年华
# 等价于
# prop.myMapPlus.k1.name = 张三
# 等价于
# prop.myMapPlus[k1][name] = 张三
# 等价于
# prop.myMapPlus.k1[name] = 张三
中文属性值乱码问题:
Spring Boot注解读取application.properties或者application-{profile}.properties文件时默认的是ISO-8859-1编码。
如果使用@PropertySource,可以设置encoding属性值为UTF-8,properties文件编码也设置为UTF-8
@Component
@ConfigurationProperties(prefix = "prop")
@PropertySource(value={"classpath:demo.properties"},encoding = "UTF-8")
@Data
public class DemoConfig {
-
2. application.yml
```java
—————————————————————普通属性测试
prop: normalString : normalString normalNumber : 456 normalBoolean : true
—————————————————————简单集合与数组测试
myListOrArray:
- d
- e
- f
等价于
myListOrArray: [d,e,f] myListOrArray: d,e,f
二维数组
myListOrArray: [[d],[e],[f]]
myListOrArray:
-
- d
- e
- f
-
- c
—————————————————————简单Map测试
myMap: k4: v4 k5: v5 k6: v6
等价于
myMap: {k4: v4,k5: v5,k6: v6}
等价于
myMap[k1]: v1
myMap[k2]: v2
myMap[k3]: v3
—————————————————————对象测试
myUser: name: 王五 age: 50 gender: 男 motto: 好好
等价于
myUser: {name: 王五,age: 50,gender: 男,motto: 好好1}
—————————————————————复杂集合与数组测试
myListOrArrayPlus: [{name: 王五,age: 50,gender: 男,motto: 好好1},{name: 王五1,age: 50,gender: 男,motto: 好好1}]
等价于
myListOrArrayPlus:
- {name: 王五,age: 50,gender: 男,motto: 好好2}
- {name: 王五1,age: 50,gender: 男,motto: 好好2}
等价于
myListOrArrayPlus:
-
name: 王五2
—————————————————————复杂Map测试
myMapPlus: k1: {name: 王五,age: 50,gender: 男,motto: 好好1} k2: {name: 王五1,age: 50,gender: 男,motto: 好好1}
myMapPlus: {k1: {name: 王五,age: 50,gender: 男,motto: 好好1},k2: {name: 王五1,age: 50,gender: 男,motto: 好好1}}
<a name="dZQ9P"></a>
#### 3. 测试
```java
@SpringBootTest
class Demo2021ApplicationTests {
@Autowired
DemoConfig demoConfig;
@Test
void contextLoads() {
System.out.println(demoConfig);
}
}
2. @ImportResource
导入Spring的配置文件,让配置文件里边的内容生效.我们在使用spring的时候,通常会用xml去配置bean然后注入数据.
这对 Spring Boot 兼容老项目非常有用,因为有些配置无法通过 Java Config 的形式来配置就只能用这个注解来导入。
3. @Configuration和@Bean
Spring新的配置体系中最为重要的构件是:@Configuration
标注的类,@Bean
标注的方法。
简单粗暴理解:@Configuration
标注的类等同于一个xml文件,@Bean
标注的方法等同于xml文件里的一个
// @since 3.0
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
/**
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
Full模式和Lite模式
Full模式和Lite模式均是针对于Spring配置类而言的,和xml配置文件无关。值得注意的是:判断是Full模式 or Lite模式的前提是,首先你得是个容器组件。
Lite模式
当@Bean
方法在没有使用@Configuration
注释的类中声明时,它们被称为在Lite模式下处理。它包括:在@Component
中声明的@Bean
方法,甚至只是在一个非常普通的类中声明的Bean方法,都被认为是Lite版的配置类。@Bean
方法是一种通用的工厂方法(factory-method)机制。
何时为Lite模式
官方定义为:在没有标注@Configuration的类里面有@Bean方法就称为Lite模式的配置。透过源码再看这个定义是不完全正确的,而应该是有如下case均认为是Lite模式的配置类:
类上标注有@Component注解
类上标注有@ComponentScan注解
类上标注有@Import注解
类上标注有@ImportResource注解
若类上没有任何注解,但类内存在@Bean方法
以上case的前提均是类上没有被标注@Configuration,在Spring 5.2之后新增了一种case也算作Lite模式:
标注有@Configuration(proxyBeanMethods = false),注意:此值默认是true哦,需要显示改为false才算是Lite模式
细心的你会发现,自Spring5.2(对应Spring Boot 2.2.0)开始,内置的几乎所有的@Configuration配置类都被修改为了@Configuration(proxyBeanMethods = false),目的何为?答:以此来降低启动时间,为Cloud Native继续做准备。
Lite模式优缺点
优点:
运行时不再需要给对应类生成CGLIB子类,提高了运行性能,降低了启动时间
可以该配置类当作一个普通类使用喽:也就是说@Bean方法 可以是private、可以是final
缺点:
不能声明@Bean之间的依赖,也就是说不能通过方法调用来依赖其它Bean
(其实这个缺点还好,很容易用其它方式“弥补”,比如:把依赖Bean放进方法入参里即可)
总结
该模式下,配置类本身不会被CGLIB增强,放进IoC容器内的就是本尊
该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
该模式下,配置类内部不能通过方法调用来处理依赖,否则每次生成的都是一个新实例而并非IoC容器内的单例
该模式下,配置类就是一普通类嘛,所以@Bean方法可以使用private/final等进行修饰(static自然也是阔仪的)
Full模式
在常见的场景中,@Bean
方法都会在标注有@Configuration
的类中声明,以确保总是使用“Full模式”,这么一来,交叉方法引用会被重定向到容器的生命周期管理,所以就可以更方便的管理Bean依赖。
何时为Full模式
标注有@Configuration注解的类被称为full模式的配置类。自Spring5.2后这句话改为下面这样我觉得更为精确些:
标注有@Configuration或者@Configuration(proxyBeanMethods = true)的类被称为Full模式的配置类
(当然喽,proxyBeanMethods属性的默认值是true,所以一般需要Full模式我们只需要标个注解即可)
Full模式优缺点
优点:
可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,这有效规避了在“Lite模式”下操作时难以跟踪的细微错误。特别对于萌新程序员,这个特点很有意义
缺点:
运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销(这个开销在Spring Boot这种拥有大量配置类的情况下是不容忽视的,这也是为何Spring 5.2新增了proxyBeanMethods属性的最直接原因)
正因为被代理了,所以@Bean方法 不可以是private、不可以是final
总结
该模式下,配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理
该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
该模式下,配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向IoC内的那个单例
该模式下,@Bean方法不能被private/final等进行修饰(很简单,因为方法需要被复写嘛,所以不能私有和final。defualt/protected/public都可以哦),否则启动报错(其实IDEA编译器在编译器就提示可以提示你了)
使用建议
- 如果是在公司的业务功能/服务上做开发,使用Full模式
- 如果你是个容器开发者,或者你在开发中间件、通用组件等,那么使用Lite模式是一种更被推荐的方式,它对Cloud Native更为友好