springboot中@Value的工作原理

springboot中@Value的工作原理

springboot版本: springboot-2.0.6.RELEASE


概述

springboot启动过程中,有两个比较重要的过程,如下:
1 扫描,解析容器中的bean注册到beanFactory上去,就像是信息登记一样。
2 实例化、初始化这些扫描到的bean。

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

解析流程

调用链时序图

@Value解析过程中的主要调用链,我用以下时序图来表示:
在这里插入图片描述
这里先简单介绍一下图上的几个类的作用。

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<InjectedElement> checkedElements = this.checkedElements;
	Collection<InjectedElement> 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<String> visitedPlaceholders) {
StringBuilder result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// this.placeholderPrefix = "${"</span>
<span class="token keyword">int</span> startIndex <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>placeholderPrefix<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span>startIndex <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">// 占位符的结束位置,以value = "${company.ceo}"为例,endIndex=13</span>
	<span class="token keyword">int</span> endIndex <span class="token operator">=</span> <span class="token function">findPlaceholderEndIndex</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> startIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>endIndex <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token comment">// 获取{}里的真正属性名称,此例为"company.ceo"</span>
		String placeholder <span class="token operator">=</span> result<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>startIndex <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>placeholderPrefix<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> endIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
		String originalPlaceholder <span class="token operator">=</span> placeholder<span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>visitedPlaceholders<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>originalPlaceholder<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span>
					<span class="token string">"Circular placeholder reference '"</span> <span class="token operator">+</span> originalPlaceholder <span class="token operator">+</span> <span class="token string">"' in property definitions"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token comment">// Recursive invocation, parsing placeholders contained in the placeholder key.</span>
		<span class="token comment">// 递归调用本方法,因为属性键中可能仍然有占位符</span>
		placeholder <span class="token operator">=</span> <span class="token function">parseStringValue</span><span class="token punctuation">(</span>placeholder<span class="token punctuation">,</span> placeholderResolver<span class="token punctuation">,</span> visitedPlaceholders<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">// Now obtain the value for the fully resolved key...</span>
		<span class="token comment">// 获取属性键placeholder对应的属性值</span>
		String propVal <span class="token operator">=</span> placeholderResolver<span class="token punctuation">.</span><span class="token function">resolvePlaceholder</span><span class="token punctuation">(</span>placeholder<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token comment">// 此处逻辑是当company.ceo=${bi:li}时,company.ceo最终被li所替代的原因</span>
		<span class="token comment">// 所以配置文件中,最好不要出现类似${}的东西,因为它本身就会被spring框架所解析</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>propVal <span class="token operator">==</span> null <span class="token operator">&amp;&amp;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>valueSeparator <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">int</span> separatorIndex <span class="token operator">=</span> placeholder<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>valueSeparator<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>separatorIndex <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
				String actualPlaceholder <span class="token operator">=</span> placeholder<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> separatorIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
				String defaultValue <span class="token operator">=</span> placeholder<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>separatorIndex <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>valueSeparator<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
				propVal <span class="token operator">=</span> placeholderResolver<span class="token punctuation">.</span><span class="token function">resolvePlaceholder</span><span class="token punctuation">(</span>actualPlaceholder<span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token keyword">if</span> <span class="token punctuation">(</span>propVal <span class="token operator">==</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span>
					propVal <span class="token operator">=</span> defaultValue<span class="token punctuation">;</span>
				<span class="token punctuation">}</span>
			<span class="token punctuation">}</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>propVal <span class="token operator">!=</span> null<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token comment">// Recursive invocation, parsing placeholders contained in the</span>
			<span class="token comment">// previously resolved placeholder value.</span>
			propVal <span class="token operator">=</span> <span class="token function">parseStringValue</span><span class="token punctuation">(</span>propVal<span class="token punctuation">,</span> placeholderResolver<span class="token punctuation">,</span> visitedPlaceholders<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token comment">// 将${company.ceo}替换为li</span>
			result<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>startIndex<span class="token punctuation">,</span> endIndex <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>placeholderSuffix<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> propVal<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
				logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Resolved placeholder '"</span> <span class="token operator">+</span> placeholder <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">}</span>
			startIndex <span class="token operator">=</span> result<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>placeholderPrefix<span class="token punctuation">,</span> startIndex <span class="token operator">+</span> propVal<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>ignoreUnresolvablePlaceholders<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token comment">// Proceed with unprocessed value.</span>
			startIndex <span class="token operator">=</span> result<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>placeholderPrefix<span class="token punctuation">,</span> endIndex <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>placeholderSuffix<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">else</span> <span class="token punctuation">{</span>
			<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Could not resolve placeholder '"</span> <span class="token operator">+</span>
					placeholder <span class="token operator">+</span> <span class="token string">"'"</span> <span class="token operator">+</span> <span class="token string">" in value \""</span> <span class="token operator">+</span> value <span class="token operator">+</span> <span class="token string">"\""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		visitedPlaceholders<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>originalPlaceholder<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">else</span> <span class="token punctuation">{</span>
		startIndex <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">return</span> result<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 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的实例化、初始化阶段完成的。

        </div>
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-7b4cdcb592.css" rel="stylesheet">
            </div>
</article>
Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过使用注解来简化开发过程,提供了一种快速、方便的方式来配置和管理Spring应用程序。 在Spring Boot,注解被广泛应用于各个方面,包括配置、依赖注入、AOP等。下面是一些常用的Spring Boot注解及其底层原理: 1. @SpringBootApplication:这是一个组合注解,包含了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。@Configuration用于定义配置类,@EnableAutoConfiguration用于自动配置Spring应用程序上下文,@ComponentScan用于扫描并加载标有@Component注解的类。 2. @RestController:这个注解用于标识一个类是RESTful风格的控制器,它将类的方法返回的数据直接以JSON或XML格式发送给客户端。 3. @RequestMapping:这个注解用于映射HTTP请求到控制器的处理方法上。可以通过指定URL路径、请求方法、请求参数等来定义具体的映射规则。 4. @Autowired:这个注解用于自动装配依赖对象。Spring Boot会根据类型进行自动查找并注入相应的实例。 5. @Value:这个注解用于从配置文件读取属性值,并将其注入到对应的字段或方法参数。 6. @Component:这个注解用于标识一个类是Spring组件,会被自动扫描并加载到Spring容器。 7. @ConfigurationProperties:这个注解用于将配置文件的属性值绑定到一个Java对象上,可以通过@ConfigurationProperties(prefix = "prefix")指定属性的前缀。 以上是一些常用的Spring Boot注解及其底层原理。通过使用这些注解,可以简化开发过程,提高开发效率。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值