1. @ConfigurationProperties

@ConfigurationProperties是springboot提供读取配置文件的一个注解。其对应的bean的后置处理器为ConfigurationPropertiesBindingPostProcessor

  1. public class ConfigurationPropertiesBindingPostProcessor
  2. implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {

它是实现了BeanPostProcessor接口,在bean被实例化后,会调用后置处理,递归的查找属性,通过反射注入值,对大多数属性而言强制需提供其setter和getter方法。
但是属性名称不要求一定相同,只需保证配置文件的属性和setter方法名相同即可。
不过@ConfigurationProperties注解,只可以把应用主配置文件的内容读取进来,要想给Bean赋值,只能读取主配置文件,就是application.properties或者application.yml文件,若我们想自己写一个配置文件来给bean赋值,我们就可以使用该注解@PropertyResource
我们在resource里边新建一个student.properties文件,并把之前主配置文件里的内容剪贴进来,并在bean上边加上注解

  1. @Component
  2. @ConfigurationProperties(prefix = "student")
  3. @PropertyResource(value = {"classpath:student.properties"})
  4. // value可以添加多个配置文件,classpath指的是类路径,表示类路径下的student.properties文件
  5. public class Student {
  6. ......
  7. }

1.@ConfigurationProperties读取配置文件,注入bean的三种方式

1. 配合@Component注解直接进行注入(主要)

  1. @Component
  2. @ConfigurationProperties(prefix = "student")
  3. public class Student {
  4. ......
  5. }

2. 使用@EnableConfigurationProperties,通常配置在标有@Configuration的类上,当然其他@Component注解的派生类也可以,不过不推荐.(摘取)(springboot源码大量使用,例如DataSourceAutoConfiguration,FreeMarkerAutoConfiguration)

  1. @ConfigurationProperties(prefix = "doc")
  2. public class DocumentServerProperties {
  3. //代码...
  4. }
  5. //@EnableConfigurationProperties的作用:使@ConfigurationProperties注解的类生效,并将标注@ConfigurationProperties注解的类注册到spring容器中
  6. //@EnableConfigurationProperties({DocumentServerProperties.class}) 只是让DocumentServerProperties这个类生效,并将其注册到spring容器中
  7. @EnableConfigurationProperties
  8. @Configuration
  9. public class SomeConfiguration {
  10. }

3. 使用@Bean方式在标有@Configuration的类进行注入,这种方式通常可以用在对第三方类进行配置属性注册(摘取)

  1. public class ThirdComponent {
  2. ...
  3. }
  4. @Configuration
  5. public class SomeConfiguration {
  6. @ConfigurationProperties("demo")
  7. @Bean
  8. public ThirdComponent thirdComponent(){
  9. return new ThirdComponent();
  10. }
  11. }

2. @ConfigurationProperties(yml/properties) 和 @Value(“xx”) 区别

image.png

3. @ConfigurationProperties绑定配置信息至Array、List、Map、Bean的实现

如果application.properties与自定义的 properties有重复的属性,取application.properties配置的属性值

  1. @Data
  2. public class User {
  3. private String name;
  4. private Integer age;
  5. private String gender;
  6. private String motto;
  7. }
  1. @Component
  2. @ConfigurationProperties(prefix = "prop")
  3. @Data
  4. public class DemoConfig {
  5. private String normalString;
  6. private Integer normalNumber;
  7. private Boolean normalBoolean;
  8. /**
  9. * 类型为数组或者集合都可以
  10. */
  11. private String[] myListOrArray;
  12. private Map<String, Object> myMap;
  13. private User myUser;
  14. /**
  15. * 类型为数组或者集合都可以
  16. */
  17. private List<User> myListOrArrayPlus;
  18. private Map<String, User> myMapPlus;
  19. }

1. application.properties

  1. #------------------------------------------普通属性测试
  2. prop.normalString = value1
  3. prop.normalNumber = 123
  4. prop.normalBoolean = false
  5. #------------------------------------------简单集合与数组测试
  6. prop.myListOrArray[0] = a
  7. prop.myListOrArray[1] = b
  8. prop.myListOrArray[2] = c
  9. # 等价于
  10. # prop.myListOrArray = a,b,c
  11. #------------------------------------------简单Map测试
  12. prop.myMap[k1] = v1
  13. prop.myMap[k2] = v2
  14. prop.myMap[k3] = v3
  15. # 等价于
  16. # prop.myMap.k1 = v1
  17. # prop.myMap.k1 = v1
  18. # prop.myMap.k1 = v1
  19. #------------------------------------------对象测试
  20. prop.myUser.name = 张三
  21. prop.myUser.[age] = 28
  22. prop.myUser.gender =
  23. prop.myUser.motto = 岁月
  24. # 等价于
  25. # prop.myUser[name] = 张三
  26. # prop.myUser[age] = 28
  27. # prop.myUser[gender] =
  28. # prop.myUser[motto] = 岁月静好
  29. #------------------------------------------复杂集合与数组测试
  30. prop.myListOrArrayPlus[0].name = 张三
  31. prop.myListOrArrayPlus[0].age = 28
  32. prop.myListOrArrayPlus[0].gender =
  33. prop.myListOrArrayPlus[0].motto = 岁月
  34. prop.myListOrArrayPlus[1].name = 李四
  35. prop.myListOrArrayPlus[1].age = 28
  36. prop.myListOrArrayPlus[1].gender =
  37. prop.myListOrArrayPlus[1].motto = 年华
  38. # 等价于
  39. # prop.myListOrArrayPlus[0][name] = 张三
  40. # prop.myListOrArrayPlus[0][age] = 28
  41. # prop.myListOrArrayPlus[0][gender] =
  42. # prop.myListOrArrayPlus[0][motto] = 岁月
  43. # prop.myListOrArrayPlus[1][name] = 李四
  44. # prop.myListOrArrayPlus[1][age] = 28
  45. # prop.myListOrArrayPlus[1][gender] =
  46. # prop.myListOrArrayPlus[1][motto] = 年华
  47. #------------------------------------------复杂Map测试
  48. prop.myMapPlus[k1].name = 张三
  49. prop.myMapPlus[k1].age = 28
  50. prop.myMapPlus[k1].gender =
  51. prop.myMapPlus[k1].motto = 岁月
  52. prop.myMapPlus[k2].name = 李四
  53. prop.myMapPlus[k2].age = 28
  54. prop.myMapPlus[k2].gender =
  55. prop.myMapPlus[k2].motto = 年华
  56. # 等价于
  57. # prop.myMapPlus.k1.name = 张三
  58. # 等价于
  59. # prop.myMapPlus[k1][name] = 张三
  60. # 等价于
  61. # prop.myMapPlus.k1[name] = 张三

中文属性值乱码问题:
Spring Boot注解读取application.properties或者application-{profile}.properties文件时默认的是ISO-8859-1编码。

  • 如果使用@PropertySource,可以设置encoding属性值为UTF-8,properties文件编码也设置为UTF-8

    1. @Component
    2. @ConfigurationProperties(prefix = "prop")
    3. @PropertySource(value={"classpath:demo.properties"},encoding = "UTF-8")
    4. @Data
    5. public class DemoConfig {
  • 在IDE的settings中设置文件编码Spring Boot 注解 - 图2

    2. application.yml

    ```java

    —————————————————————普通属性测试

    prop: normalString : normalString normalNumber : 456 normalBoolean : true

—————————————————————简单集合与数组测试

myListOrArray:

  1. - d
  2. - e
  3. - 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}}

  1. <a name="dZQ9P"></a>
  2. #### 3. 测试
  3. ```java
  4. @SpringBootTest
  5. class Demo2021ApplicationTests {
  6. @Autowired
  7. DemoConfig demoConfig;
  8. @Test
  9. void contextLoads() {
  10. System.out.println(demoConfig);
  11. }
  12. }

2. @ImportResource

导入Spring的配置文件,让配置文件里边的内容生效.我们在使用spring的时候,通常会用xml去配置bean然后注入数据.
这对 Spring Boot 兼容老项目非常有用,因为有些配置无法通过 Java Config 的形式来配置就只能用这个注解来导入。

3. @Configuration和@Bean

Spring新的配置体系中最为重要的构件是:@Configuration标注的类,@Bean标注的方法。
简单粗暴理解:@Configuration标注的类等同于一个xml文件,@Bean标注的方法等同于xml文件里的一个标签

  1. // @since 3.0
  2. @Target(ElementType.TYPE)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Documented
  5. @Component
  6. public @interface Configuration {
  7. @AliasFor(annotation = Component.class)
  8. String value() default "";
  9. /**
  10. * @since 5.2
  11. */
  12. boolean proxyBeanMethods() default true;
  13. }

Full模式和Lite模式

Full模式和Lite模式均是针对于Spring配置类而言的,和xml配置文件无关。值得注意的是:判断是Full模式 or Lite模式的前提是,首先你得是个容器组件。

Lite模式

@Bean方法在没有使用@Configuration注释的类中声明时,它们被称为在Lite模式下处理。它包括:在@Component中声明的@Bean方法,甚至只是在一个非常普通的类中声明的Bean方法,都被认为是Lite版的配置类。@Bean方法是一种通用的工厂方法(factory-method)机制。

何时为Lite模式
  1. 官方定义为:在没有标注@Configuration的类里面有@Bean方法就称为Lite模式的配置。透过源码再看这个定义是不完全正确的,而应该是有如下case均认为是Lite模式的配置类:
  2. 类上标注有@Component注解
  3. 类上标注有@ComponentScan注解
  4. 类上标注有@Import注解
  5. 类上标注有@ImportResource注解
  6. 若类上没有任何注解,但类内存在@Bean方法
  7. 以上case的前提均是类上没有被标注@Configuration,在Spring 5.2之后新增了一种case也算作Lite模式:
  8. 标注有@Configuration(proxyBeanMethods = false),注意:此值默认是true哦,需要显示改为false才算是Lite模式
  9. 细心的你会发现,自Spring5.2(对应Spring Boot 2.2.0)开始,内置的几乎所有的@Configuration配置类都被修改为了@Configuration(proxyBeanMethods = false),目的何为?答:以此来降低启动时间,为Cloud Native继续做准备。

Lite模式优缺点
  1. 优点:
  2. 运行时不再需要给对应类生成CGLIB子类,提高了运行性能,降低了启动时间
  3. 可以该配置类当作一个普通类使用喽:也就是说@Bean方法 可以是private、可以是final
  4. 缺点:
  5. 不能声明@Bean之间的依赖,也就是说不能通过方法调用来依赖其它Bean
  6. (其实这个缺点还好,很容易用其它方式“弥补”,比如:把依赖Bean放进方法入参里即可)

总结
  1. 该模式下,配置类本身不会被CGLIB增强,放进IoC容器内的就是本尊
  2. 该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
  3. 该模式下,配置类内部不能通过方法调用来处理依赖,否则每次生成的都是一个新实例而并非IoC容器内的单例
  4. 该模式下,配置类就是一普通类嘛,所以@Bean方法可以使用private/final等进行修饰(static自然也是阔仪的)

Full模式

在常见的场景中,@Bean方法都会在标注有@Configuration的类中声明,以确保总是使用“Full模式”,这么一来,交叉方法引用会被重定向到容器的生命周期管理,所以就可以更方便的管理Bean依赖。

何时为Full模式
  1. 标注有@Configuration注解的类被称为full模式的配置类。自Spring5.2后这句话改为下面这样我觉得更为精确些:
  2. 标注有@Configuration或者@Configuration(proxyBeanMethods = true)的类被称为Full模式的配置类
  3. (当然喽,proxyBeanMethods属性的默认值是true,所以一般需要Full模式我们只需要标个注解即可)

Full模式优缺点
  1. 优点:
  2. 可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,这有效规避了在“Lite模式”下操作时难以跟踪的细微错误。特别对于萌新程序员,这个特点很有意义
  3. 缺点:
  4. 运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销(这个开销在Spring Boot这种拥有大量配置类的情况下是不容忽视的,这也是为何Spring 5.2新增了proxyBeanMethods属性的最直接原因)
  5. 正因为被代理了,所以@Bean方法 不可以是private、不可以是final

总结
  1. 该模式下,配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理
  2. 该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
  3. 该模式下,配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向IoC内的那个单例
  4. 该模式下,@Bean方法不能被private/final等进行修饰(很简单,因为方法需要被复写嘛,所以不能私有和finaldefualt/protected/public都可以哦),否则启动报错(其实IDEA编译器在编译器就提示可以提示你了)

使用建议

  • 如果是在公司的业务功能/服务上做开发,使用Full模式
  • 如果你是个容器开发者,或者你在开发中间件、通用组件等,那么使用Lite模式是一种更被推荐的方式,它对Cloud Native更为友好