springboot 成员变量_springboot中@Value的工作原理

springboot中@Value的工作原理

2018年12月31日 16:38:21 gs_albb

阅读数:181

版权声明:本文为博主原创文章,未经博主允许不得转载https://blog.csdn.net/gs_albb/article/details/85401720

我们知道springboot中的Bean组件的成员变量(属性)如果加上了@Value注解,可以从有效的配置属性资源中找到配置项进行绑定,那么这一切是怎么发生的呢?下文将简要分析一下@Value的工作原理。

springboot版本: springboot-2.0.6.RELEASE

概述

springboot启动过程中,有两个比较重要的过程,如下:

1 扫描,解析容器中的bean注册到beanFactory上去,就像是信息登记一样。

2 实例化、初始化这些扫描到的bean。

@Value的解析就是在第二个阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类AutowiredAnnotationBeanPostProcessor正如javadoc所说的那样,为bean中的@Autowired和@Value注解的注入功能提供支持。

解析流程

调用链时序图

@Value解析过程中的主要调用链,我用以下时序图来表示:

7b81099267196d27324de31b01056090.png

这里先简单介绍一下图上的几个类的作用。

AbstractAutowireCapableBeanFactory: 提供了bean创建,属性填充,自动装配,初始胡。支持自动装配构造函数,属性按名称和类型装配。实现了AutowireCapableBeanFactory接口定义的createBean方法。

AutowiredAnnotationBeanPostProcessor: 装配bean中使用注解标注的成员变量,setter方法, 任意的配置方法。比较典型的是@Autowired注解和@Value注解。

InjectionMetadata: 类的注入元数据,可能是类的方法或属性等,在AutowiredAnnotationBeanPostProcessor类中被使用。

AutowiredFieldElement: 是AutowiredAnnotationBeanPostProcessor的一个私有内部类,继承InjectionMetadata.InjectedElement,描述注解的字段。

StringValueResolver: 一个定义了处置字符串值的接口,只有一个接口方法resolveStringValue,可以用来解决占位符字符串。本文中的主要实现类在PropertySourcesPlaceholderConfigurer#processProperties方法中通过lamda表达式定义的。供ConfigurableBeanFactory类使用。

PropertySourcesPropertyResolver: 属性资源处理器,主要功能是获取PropertySources属性资源中的配置键值对。

PropertyPlaceholderHelper: 一个工具类,用来处理带有占位符的字符串。形如${name}的字符串在该工具类的帮助下,可以被用户提供的值所替代。替代途经可能通过Properties实例或者PlaceholderResolver(内部定义的接口)。

PropertyPlaceholderConfigurerResolver: 上一行所说的PlaceholderResolver接口的一个实现类,是PropertyPlaceholderConfigurer类的一个私有内部类。实现方法resolvePlaceholder中调用了外部类的resolvePlaceholder方法。

调用链说明

这里主要介绍一下调用链中的比较重要的方法。

AbstractAutowireCapableBeanFactory#populateBean方法用于填充bean属性,执行完后可获取属性装配后的bean。

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {

...

if (hasInstAwareBpps) {

// 遍历所有InstantiationAwareBeanPostProcessor实例设置属性字段值。

for (BeanPostProcessor bp : getBeanPostProcessors()) {

// AutowiredAnnotationBeanPostProcessor会进入此分支

if (bp instanceof InstantiationAwareBeanPostProcessor) {

InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

//上行代码执行后,bw.getWrappedInstance()就得到了@Value注解装配属性后的bean了

if (pvs == null) {

return;

}

}

}

}

...

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

InjectionMetadata#inject逐个装配bean的配置属性。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {

Collection checkedElements = this.checkedElements;

Collection elementsToIterate =

(checkedElements != null ? checkedElements : this.injectedElements);

if (!elementsToIterate.isEmpty()) {

// 依次注入属性

for (InjectedElement element : elementsToIterate) {

if (logger.isDebugEnabled()) {

logger.debug("Processing injected element of bean '" + beanName + "': " + element);

}

element.inject(target, beanName, pvs);

}

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

PropertyPlaceholderHelper#parseStringValue解析属性值

/**

* 一个参数示例 value = "${company.ceo}"

*

*/

protected String parseStringValue(

String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders) {StringBuilder result = new StringBuilder(value);

// this.placeholderPrefix = "${"

int startIndex = value.indexOf(this.placeholderPrefix);

while (startIndex != -1) {

// 占位符的结束位置,以value = "${company.ceo}"为例,endIndex=13

int endIndex = findPlaceholderEndIndex(result, startIndex);

if (endIndex != -1) {

// 获取{}里的真正属性名称,此例为"company.ceo"

String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);

String originalPlaceholder = placeholder;

if (!visitedPlaceholders.add(originalPlaceholder)) {

throw new IllegalArgumentException(

"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");

}

// Recursive invocation, parsing placeholders contained in the placeholder key.

// 递归调用本方法,因为属性键中可能仍然有占位符

placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);

// Now obtain the value for the fully resolved key...

// 获取属性键placeholder对应的属性值

String propVal = placeholderResolver.resolvePlaceholder(placeholder);

// 此处逻辑是当company.ceo=${bi:li}时,company.ceo最终被li所替代的原因

// 所以配置文件中,最好不要出现类似${}的东西,因为它本身就会被spring框架所解析

if (propVal == null && this.valueSeparator != null) {

int separatorIndex = placeholder.indexOf(this.valueSeparator);

if (separatorIndex != -1) {

String actualPlaceholder = placeholder.substring(0, separatorIndex);

String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());

propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);

if (propVal == null) {

propVal = defaultValue;

}

}

}

if (propVal != null) {

// Recursive invocation, parsing placeholders contained in the

// previously resolved placeholder value.

propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);

// 将${company.ceo}替换为li

result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);

if (logger.isTraceEnabled()) {

logger.trace("Resolved placeholder '" + placeholder + "'");

}

startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());

}

else if (this.ignoreUnresolvablePlaceholders) {

// Proceed with unprocessed value.

startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());

}

else {

throw new IllegalArgumentException("Could not resolve placeholder '" +

placeholder + "'" + " in value \"" + value + "\"");

}

visitedPlaceholders.remove(originalPlaceholder);

}

else {

startIndex = -1;

}

}

return result.toString();

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

总结

@Value注解标注的bean属性装配是依靠AutowiredAnnotationBeanPostProcessor在bean的实例化、初始化阶段完成的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值