深度解析@Value注解,你真的徹底了解過嗎?
一、學(xué)習(xí)指引
Spring中的@Value注解,你真的徹底了解過嗎?
在實(shí)際開發(fā)過程中,通常會有這樣一種場景:將一些配置項寫到配置文件中,在業(yè)務(wù)邏輯中會讀取配置文件中的配置項,取出對應(yīng)的值進(jìn)行業(yè)務(wù)邏輯處理。Spring中提供的@Value注解就可以讀取配置文件中的值。另外@Value注解也可以向Bean中的屬性設(shè)置其他值。本章,就對@Value注解進(jìn)行簡單的介紹。
二、注解說明
關(guān)于@Value注解的一點(diǎn)點(diǎn)說明~~
@Value注解可以向Spring的Bean的屬性中注入數(shù)據(jù)。并且支持Spring的EL表達(dá)式,可以通過${} 的方式獲取配置文件中的數(shù)據(jù)。配置文件支持properties、XML、和YML文件。
1、注解源碼
@Value注解的源碼詳見:org.springframework.beans.factory.annotation.Value。
/**
* @author Juergen Hoeller
* @since 3.0
* @see AutowiredAnnotationBeanPostProcessor
* @see Autowired
* @see org.springframework.beans.factory.config.BeanExpressionResolver
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}
從源碼可以看出,@Value注解可以標(biāo)注到字段、方法、參數(shù)和其他注解上,@Value注解中提供了一個String類型的value屬性,具體含義如下所示。
- value:指定要向Bean的屬性中注入的數(shù)據(jù),數(shù)據(jù)可以是配置文件中的配置項,并且支持EL表達(dá)式。
2、使用場景
在實(shí)際開發(fā)中,項目中難免會有一些配置信息,此時,就可以將這些配置信息統(tǒng)一寫到配置文件中。隨后使用@Value注解讀取配置文件的值來向Spring中Bean的屬性設(shè)置值。
例如,一些系統(tǒng)環(huán)境變量信息,數(shù)據(jù)庫配置,系統(tǒng)通用配置等等,都可以保存到配置文件中,此時就可以使用Spring的EL表達(dá)式讀取配置文件中的值。
3、用法
本節(jié),主要介紹不通過配置文件注入屬性和通過配置文件注入屬性兩種情況來介紹@Value注解的用法。
不通過配置文件注入屬性
通過@Value可以將外部的值動態(tài)注入到Bean中,有如下幾種用法。
(1)注入普通字符串。
@Value("normalString")
private String normalString;
(2)注入操作系統(tǒng)屬性。
@Value("#{systemProperties['os.name']}")
private String osName;
(3)注入表達(dá)式的結(jié)果信息。
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNum;
(4)注入其他Bean屬性。
@Value("#{otherBean.name}")
private String name;
(5)注入文件資源。
@Value("classpath:config.properties")
private Resource resourceFile;
(6)注入URL資源。
@Value("http://www.baidu.com")
private Resource url;
通過配置文件注入屬性
通過@Value(“${app.name}”)語法將屬性文件的值注入到bean的屬性中,
@Component
@PropertySource({"classpath:config.properties","classpath:config_${anotherfile.configinject}.properties"})
public class ConfigurationFileInject{
@Value("${user.id}")
private String userId;
@Value("${user.name}")
private String userName;
@Value("${user.address}")
private String userAddress;
}
@Value中#{...}和${...}的區(qū)別
這里提供一個測試屬性文件:test.properties,大致的內(nèi)容如下所示。
server.name=server1,server2,server3
author.name=binghe
測試類Test:引入test.properties文件,作為屬性的注入。
@Component
@PropertySource({"classpath:test.properties"})
public class Test {
}
${...}的用法
{}里面的內(nèi)容必須符合SpEL表達(dá)式, 通過@Value(“${spelDefault.value}”)可以獲取屬性文件中對應(yīng)的值,但是如果屬性文件中沒有這個屬性,則會報錯??梢酝ㄟ^賦予默認(rèn)值解決這個問題,如下所示。
@Value("${author.name:binghe}")
上述代碼的含義表示向Bean的屬性中注入配置文件中的author.name屬性的值,如果配置文件中沒有author.name屬性,則向Bean的屬性中注入默認(rèn)值binghe。例如下面的代碼片段。
@Value("${author.name:binghe}")
private String name;
#{…}的用法
(1)SpEL:調(diào)用字符串Hello World的concat方法
@Value("#{'Hello World'.concat('!')}")
private String helloWorld;
(2)SpEL: 調(diào)用字符串的getBytes方法,然后調(diào)用length屬性
@Value("#{'Hello World'.bytes.length}")
private int length;
${…}和#{…}混合使用
${...}和#{...}可以混合使用,如下文代碼執(zhí)行順序:傳入一個字符串,根據(jù) "," 切分后插入列表中, #{}和${}配合使用,注意單引號。
@Value("#{'${server.name}'.split(',')}")
private List<String> servers;
注意:${}和#{}混合實(shí)用時,不能${}在外面,#{}在里面。因為Spring執(zhí)行${}的時機(jī)要早于#{},當(dāng)Spring執(zhí)行外層的${}時,內(nèi)部的#{}為空,會執(zhí)行失敗。
@Value注解用法總結(jié)
- #{…} 用于執(zhí)行SpEl表達(dá)式,并將內(nèi)容賦值給屬性。
- ${…} 主要用于加載外部屬性文件中的值。
- #{…} 和${…} 可以混合使用,但是必須#{}外面,${}在里面。
三、使用案例
@Value的實(shí)現(xiàn)案例,我們一起實(shí)現(xiàn)吧~~
本節(jié),就基于@Value注解實(shí)現(xiàn)向Bean屬性中賦值的案例,具體的實(shí)現(xiàn)步驟如下所示。
1、新增test.properties配置文件
在spring-annotation-chapter-11工程下的resources目錄下新增test.properties配置文件,內(nèi)容如下所示。
db.url=jdbc:mysql://localhost:3306/test
2、新增ValueName類
ValueName類的源碼詳見:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.bean.ValueName。
@Component
public class ValueName {
private String name;
public ValueName() {
this.name = "binghe";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
可以看到,ValueName類上標(biāo)注了@Component注解,說明當(dāng)Spring的IOC容器啟動時,會向IOC容器中注入ValueName類的Bean對象。
3、新增ValueConfig類
ValueConfig類的源碼詳見:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.config.ValueConfig。
@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter11"})
@PropertySource(value = {"classpath:test.properties"})
public class ValueConfig {
/**
* 注入普通字符串
*/
@Value("normalString")
private String normalString;
/**
* 注入操作系統(tǒng)名稱
*/
@Value("#{systemProperties['os.name']}")
private String osName;
/**
* 注入表達(dá)式的結(jié)果
*/
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNum;
/**
* 注入其他Bean的屬性
*/
@Value("#{valueName.name}")
private String name;
/**
* 注入配置文件中的值
*/
@Value("${db.url}")
private String dbUrl;
@Override
public String toString() {
return "ValueConfig{" +
"normalString='" + normalString + '\'' +
", osName='" + osName + '\'' +
", randomNum=" + randomNum +
", name='" + name + '\'' +
", dbUrl='" + dbUrl + '\'' +
'}';
}
}
可以看到,在ValueConfig類上標(biāo)注了@Configuration注解,說明ValueConfig類是Spring的配置類。使用@ComponentScan注解指定了掃描的包名是io.binghe.spring.annotation.chapter11。并且使用@PropertySource注解導(dǎo)入了test.properties配置文件。ValueConfig類的字段通過@Value注解注入對應(yīng)的屬性值,代碼中有詳細(xì)的注釋,這里不再贅述。
4、新增ValueTest類
ValueTest類的源碼詳見:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.ValueTest。
public class ValueTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ValueConfig.class);
ValueConfig valueConfig = context.getBean(ValueConfig.class);
System.out.println(valueConfig.toString());
}
}
可以看到,ValueTest類是案例程序的測試類,實(shí)現(xiàn)的代碼比較簡單,這里不再贅述。
5、運(yùn)行ValueTest類
運(yùn)行ValueTest類的main()方法,輸出的結(jié)果信息如下所示。
ValueConfig{normalString='normalString', osName='Windows 10', randomNum=60.704013358598715, name='binghe', dbUrl='jdbc:mysql://localhost:3306/test'}
可以看到,在ValueTest類中的各個字段值都輸出了正確的結(jié)果數(shù)據(jù)。
說明:使用@Value注解向Bean的屬性中正確設(shè)置了值。
四、源碼時序圖
結(jié)合時序圖理解源碼會事半功倍,你覺得呢?
本節(jié),就以源碼時序圖的方式,直觀的感受下@Value注解在Spring源碼層面的執(zhí)行流程。本節(jié),會從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分析源碼時序圖。
注意:本節(jié)以單例Bean為例分析源碼時序圖,并且基于@Value注解標(biāo)注到類的字段上的源碼時序圖為例進(jìn)行分析,@Value注解標(biāo)注到類的方法上的源碼時序圖與標(biāo)注到字段上的源碼時序圖基本相同,不再贅述。
1、解析并獲取@Value修飾的屬性
本節(jié),就簡單介紹下解析并獲取@Value修飾的屬性的源碼時序圖,整體如圖11-1~11-2所示。
由圖11-1~11-2可以看出,解析并獲取@Value修飾的屬性的流程中涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類和AutowiredAnnotationBeanPostProcessor類。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。
2、為@Value修飾的屬性賦值
本節(jié),就簡單介紹下為@Value修飾的屬性賦值的源碼時序圖,整體如圖11-3~11-4所示。
由圖11-3~11-4所示,為@Value修飾的屬性賦值流程涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類、AutowiredAnnotationBeanPostProcessor類、InjectionMetadata類和AutowiredFieldElement類。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。
3、使用@Value獲取屬性的值
本節(jié),就簡單介紹下使用@Value注解獲取屬性的值的源碼時序圖,整體如圖11-5~11-7所示。
由圖11-5~11-7所示,使用@Value獲取屬性的值的流程涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類、AutowiredAnnotationBeanPostProcessor類、InjectionMetadata類、AutowiredFieldElement類、AbstractEnvironment類、AbstractPropertyResolver類、PropertyPlaceholderHelper類和PropertySourcesPropertyResolver類。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。
五、源碼解析
源碼時序圖整清楚了,那就整源碼解析唄!
本節(jié),主要分析@Value注解在Spring源碼層面的執(zhí)行流程,同樣的,本節(jié)也會從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分析源碼執(zhí)行流程,并且結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻。
注意:本節(jié)以單例Bean為例分析,并且基于@Value注解標(biāo)注到類的字段上的源碼流程為例進(jìn)行分析,@Value注解標(biāo)注到類的方法上的源碼流程與標(biāo)注到字段上的源碼流程基本相同,不再贅述。
1、解析并獲取@Value修飾的屬性
本節(jié)主要對解析并獲取 @Value 修飾屬性的源碼流程進(jìn)行簡單的分析,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻,本節(jié)的源碼執(zhí)行流程可以結(jié)合圖11-1~11-2進(jìn)行理解。具體分析步驟如下所示。
注意:解析并獲取 @Value 修飾屬性源碼流程的前半部分與第7章5.3節(jié)分析源碼的流程相同,這里,從AbstractBeanFactory類的doGetBean()方法開始分析。
(1)解析AbstractBeanFactory類的doGetBean(String name, ClassrequiredType, Object[] args, boolean typeCheckOnly)方法
源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, ClassrequiredType, Object[] args, boolean typeCheckOnly)。重點(diǎn)關(guān)注如下代碼片段。
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
/***********省略其他代碼***********/
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
/***********省略其他代碼***********/
return adaptBeanInstance(name, beanInstance, requiredType);
}
可以看到,在AbstractBeanFactory類的doGetBean()方法中,如果是單例Bean,會調(diào)用getSingleton()方法創(chuàng)建單例Bean,實(shí)際執(zhí)行的是Lambda表達(dá)式中的createBean()方法來創(chuàng)建單例Bean。
(2)解析AbstractAutowireCapableBeanFactory類的createBean(String beanName, RootBeanDefinition mbd, Object[] args)。
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
/**************省略其他代碼***************/
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
可以看到,在AbstractAutowireCapableBeanFactory類的createBean()方法中,會調(diào)用doCreateBean()方法創(chuàng)建Bean對象。
(3)解析AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。此時重點(diǎn)關(guān)注創(chuàng)建Bean實(shí)例的代碼片段,如下所示。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
/***********省略其他代碼**********/
return exposedObject;
}
(4)解析AbstractAutowireCapableBeanFactory類的(String beanName, RootBeanDefinition mbd, Object[] args)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
return instantiateBean(beanName, mbd);
}
可以看到,createBeanInstance()方法會創(chuàng)建Bean的實(shí)例并返回BeanWrapper對象。
(5)返回AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法,此時,重點(diǎn)關(guān)注如下代碼片段。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
/*************省略其他代碼***************/
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
}
mbd.markAsPostProcessed();
}
}
/*************省略其他代碼***************/
}
可以看到,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中會調(diào)用applyMergedBeanDefinitionPostProcessors()方法的主要作用就是:獲取@Value、@Autowired、@PostConstruct、@PreDestroy等注解標(biāo)注的字段和方法,然后封裝到InjectionMetadata對象中,最后將所有的InjectionMetadata對象存入injectionMeatadataCache緩存中。
(6)解析AbstractAutowireCapableBeanFactory類的applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
可以看到,在AbstractAutowireCapableBeanFactory類的applyMergedBeanDefinitionPostProcessors()方法中,會調(diào)用processor的postProcessMergedBeanDefinition()方法處理BeanDefinition信息。
(7)解析AutowiredAnnotationBeanPostProcessor類postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)。
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
findInjectionMetadata(beanName, beanType, beanDefinition);
}
可以看到,在AutowiredAnnotationBeanPostProcessor類postProcessMergedBeanDefinition()方法中會調(diào)用findInjectionMetadata()方法來獲取標(biāo)注了注解的字段或者方法。
(8)解析AutowiredAnnotationBeanPostProcessor類的findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition)
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition)。
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
return metadata;
}
可以看到,在AutowiredAnnotationBeanPostProcessor類的findInjectionMetadata()方法中,調(diào)用了findAutowiringMetadata方法來解析并獲取@Value、@Autowired、@Inject等注解修飾的屬性或者方法。
(9)解析AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs)。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata()方法需要重點(diǎn)關(guān)注下,findAutowiringMetadata()方法最核心的功能就是對傳遞進(jìn)來的每個類進(jìn)行篩選判斷是否被@Value、@Autowired、@Inject注解修飾的方法或者屬性,如果是被 @Value、@Autowired、@Inject注解修飾的方法或者屬性,就會將這個類記錄下來,存入injectionMetadataCache緩存中,為后續(xù)的 DI 依賴注作準(zhǔn)備。
首次調(diào)用findAutowiringMetadata()方法時,會調(diào)用buildAutowiringMetadata()方法來查找使用@Value、@Autowired、@Inject注解修飾的方法或者屬性。
(10)解析AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata(Class<?> clazz)方法。
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata(Class<?> clazz)。方法中會查找@Value、@Autowired、@Inject注解修飾的方法或者屬性,這里以查找屬性為例,重點(diǎn)關(guān)注如下代碼片段。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
/**************省略其他代碼****************/
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
可以看到,在AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata()方法中,獲取到類上所有的字段,然后遍歷每個字段,判斷是否標(biāo)注了 @Value、@Autowired和@Inject注解,如果標(biāo)注了 @Value、@Autowired和@Inject注解,直接封裝成 AutowiredFieldElement 對象,然后保存到一個名為 currElements集合中。
指的一提的是,如果解析到的字段是靜態(tài)字段,則直接返回,這就是為什么Spring不會對類中的靜態(tài)字段賦值的原因。如下代碼片段所示。
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
在AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata()方法的最后,則將標(biāo)注了@Value、@Autowired和@Inject注解的字段封裝到 InjectionMetadata 對象中,如下所示。
return InjectionMetadata.forElements(elements, clazz);
最終回到AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata()方法中,將InjectionMetadata 對象存入injectionMetadataCache緩存中。如下所示。
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
另外,在AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata()方法中,調(diào)用了findAutowiredAnnotation()方法來獲取注解信息,如下所示。
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
接下來,看看這個findAutowiredAnnotation()方法。
(11)解析AutowiredAnnotationBeanPostProcessor類的findAutowiredAnnotation(AccessibleObject ao)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation(AccessibleObject ao)。
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
可以看到,在AutowiredAnnotationBeanPostProcessor類的findAutowiredAnnotation()方法中,會遍歷autowiredAnnotationTypes集合,通過遍歷出的每個autowiredAnnotationTypes集合中的元素從annotations中獲取MergedAnnotation對象annotation,如果annotation存在,則返回annotation。否則返回null。
這里,需要關(guān)注下autowiredAnnotationTypes集合,在AutowiredAnnotationBeanPostProcessor類的構(gòu)造方法中向autowiredAnnotationTypes集合中添加元素,如下所示。
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// jakarta.inject API not available - simply skip.
}
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// javax.inject API not available - simply skip.
}
}
可以看到,在AutowiredAnnotationBeanPostProcessor類的構(gòu)造方法中,向autowiredAnnotationTypes集合中添加了@Autowired注解、@Value注解和@Inject注解。所以,@Autowired注解賦值的流程和@Value注解賦值的流程基本一致。
至此,解析并獲取@Value注解修飾的屬性的源碼流程分析完畢。
2、為@Value修飾的屬性賦值
本節(jié)主要對為@Value修飾的屬性賦值的源碼流程進(jìn)行簡單的分析,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻,本節(jié)的源碼執(zhí)行流程可以結(jié)合圖11-3~11-4進(jìn)行理解。具體分析步驟如下所示。
注意:為@Value修飾的屬性賦值的源碼流程的前半部分與本章5.1節(jié)分析源碼的流程相同,這里,同樣從AbstractAutowireCapableBeanFactory類的doCreateBean()方法開始分析。
(1)解析AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。重點(diǎn)關(guān)注如下代碼片段。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
/************省略其他代碼*************/
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
/************省略其他代碼*************/
return exposedObject;
}
可以看到,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中,會調(diào)用populateBean方法為Bean的屬性賦值。
(2)解析AbstractAutowireCapableBeanFactory類的populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
/**************省略其他代碼*************/
if (hasInstantiationAwareBeanPostProcessors()) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
pvs = pvsToUse;
}
}
/**************省略其他代碼*************/
}
可以看到,在populateBean()方法中,會調(diào)用InstantiationAwareBeanPostProcessor類的postProcessProperties()方法來處理屬性或方法的值。實(shí)際上是調(diào)用的AutowiredAnnotationBeanPostProcessor類的postProcessProperties()方法。
(3)解析AutowiredAnnotationBeanPostProcessor類的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties(PropertyValues pvs, Object bean, String beanName)。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
可以看到,在AutowiredAnnotationBeanPostProcessor類的postProcessProperties()方法中,會調(diào)用findAutowiringMetadata()方法獲取注解的元數(shù)據(jù)信息。
(4)解析AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
由于在之前解析并獲取@Value修飾的屬性的代碼流程中,已經(jīng)完成了對@Value 修飾的屬性的獲取工作。所以,程序執(zhí)行到findAutowiringMetadata()方法內(nèi)部時,injectionMetadataCache緩存中已經(jīng)有數(shù)據(jù)了。
(5)返回AutowiredAnnotationBeanPostProcessor類的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法。在AutowiredAnnotationBeanPostProcessor類的postProcessProperties()方法中,調(diào)用了metadata對象的inject()方法為屬性賦值。
(6)解析InjectionMetadata類的inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)方法
源碼詳見:org.springframework.beans.factory.annotation.InjectionMetadata#inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)。
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) {
element.inject(target, beanName, pvs);
}
}
}
可以看到,在InjectionMetadata類的inject()方法中,會循環(huán)遍歷checkedElements集合,調(diào)用遍歷出的每個InjectedElement對象的inject()方法為屬性賦值。
注意:調(diào)用InjectedElement對象的inject()方法時,實(shí)際上可能會調(diào)用AutowiredFieldElement類的inject()方法、AutowiredMethodElement類的inject()方法或者InjectedElement類的inject()方法。這里,以調(diào)用AutowiredFieldElement類的inject()方法為例進(jìn)行說明。
(7)解析AutowiredFieldElement類的inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
value = resolveFieldValue(field, bean, beanName);
}
}
else {
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
可以看到,在AutowiredFieldElement類的inject()方法中,會調(diào)用resolveFieldValue()方法來獲取對應(yīng)的屬性值,如下所示。
value = resolveFieldValue(field, bean, beanName);
并通過反射向使用@Value注解標(biāo)注的字段賦值,如下所示。
field.set(bean, value);
(8)返回AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法。再來看下源碼:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
/************省略其他代碼*************/
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
/************省略其他代碼*************/
return exposedObject;
}
可以看到,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中,為Bean的屬性賦值后會調(diào)用initializeBean()方法對Bean進(jìn)行初始化。
(9)解析AbstractAutowireCapableBeanFactory類的initializeBean(String beanName, Object bean, RootBeanDefinition mbd)方法
源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(String beanName, Object bean, RootBeanDefinition mbd)。
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
可以看到,在AbstractAutowireCapableBeanFactory類的initializeBean()方法中,會調(diào)用applyBeanPostProcessorsBeforeInitialization()方法在初始化之前執(zhí)行一些邏輯,然后調(diào)用invokeInitMethods()執(zhí)行真正的初始化操作,執(zhí)行完Bean的初始化,會調(diào)用applyBeanPostProcessorsAfterInitialization()方法執(zhí)行初始化之后的一些邏輯。
至此,為@Value修飾的屬性賦值的源碼流程分析完畢。
3、使用@Value獲取屬性的值
本節(jié)主要對使用@Value獲取屬性的值的源碼流程進(jìn)行簡單的分析,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻,本節(jié)的源碼執(zhí)行流程可以結(jié)合圖11-5~11-7進(jìn)行理解。具體分析步驟如下所示。
注意:使用@Value獲取屬性的值的源碼流程的前半部分與本章5.2節(jié)分析源碼的流程相同,這里,從AutowiredFieldElement類的inject()方法開始分析。
(1)解析AutowiredFieldElement類的inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
else {
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
可以看到,在AutowiredFieldElement類的inject()方法中,會調(diào)用resolveFieldValue()方法處理獲取屬性的值。
(2)解析AutowiredFieldElement類的resolveFieldValue(Field field, Object bean, @Nullable String beanName)方法
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue(Field field, Object bean, @Nullable String beanName)。
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
/*************省略其他代碼************/
Object value;
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
/*************省略其他代碼************/
return value;
}
可以看到,在AutowiredFieldElement類的resolveFieldValue()方法中,會調(diào)用beanFactory對象的resolveDependency()方法,繼續(xù)向下分析。
(3)解析DefaultListableBeanFactory類的resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)
源碼詳見:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)。
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
/***************省略其他代碼**************/
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
可以看到,在DefaultListableBeanFactory類的resolveDependency()方法中,會調(diào)用doResolveDependency()方法進(jìn)一步處理。
(4)解析DefaultListableBeanFactory類的doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)方法
源碼詳見:org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)。
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
/************省略其他代碼*************/
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String strValue) {
String resolvedValue = resolveEmbeddedValue(strValue);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(resolvedValue, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
/************省略其他代碼*************/
}
可以看到,在DefaultListableBeanFactory類的doResolveDependency()方法中,如果當(dāng)前獲取到的數(shù)據(jù)是String類型,則調(diào)用resolveEmbeddedValue()方法進(jìn)行處理。
(5)解析AbstractBeanFactory類的resolveEmbeddedValue(@Nullable String value)方法
源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue(@Nullable String value)。
@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
if (value == null) {
return null;
}
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
}
return result;
}
可以看到,在AbstractBeanFactory類的resolveEmbeddedValue()中,會調(diào)用遍歷出來的StringValueResolver對象的resolveStringValue()方法進(jìn)行處理。此時,會進(jìn)入AbstractEnvironment類的resolvePlaceholders(String text)方法。
(6)解析AbstractEnvironment類的resolvePlaceholders(String text)方法
源碼詳見:org.springframework.core.env.AbstractEnvironment#resolvePlaceholders(String text)。
@Override
public String resolvePlaceholders(String text) {
return this.propertyResolver.resolvePlaceholders(text);
}
可以看到,在AbstractEnvironment類的resolvePlaceholders()方法中,會調(diào)用propertyResolver對象的resolvePlaceholders()方法進(jìn)行處理。
(7)解析AbstractPropertyResolver類的resolvePlaceholders(String text)方法
源碼詳見:org.springframework.core.env.AbstractPropertyResolver#resolvePlaceholders(String text)。
@Override
public String resolvePlaceholders(String text) {
if (this.nonStrictHelper == null) {
this.nonStrictHelper = createPlaceholderHelper(true);
}
return doResolvePlaceholders(text, this.nonStrictHelper);
}
可以看到,在AbstractPropertyResolver類的resolvePlaceholders()方法中,會調(diào)用doResolvePlaceholders()方法進(jìn)一步處理。
(8)解析AbstractPropertyResolver類的doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)方法
源碼詳見:org.springframework.core.env.AbstractPropertyResolver#doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)。
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
可以看到,在AbstractPropertyResolver類的doResolvePlaceholders()方法中,會解析 ${xxx.xxx} 這種占位,最終獲取到 key = xxx.xxx,隨后根據(jù)key去資源文件(xml、application.properties、Environment 等)中查找是否配置了這個key的值。其實(shí)是通過調(diào)用helper的replacePlaceholders()方法并以Lambda表達(dá)式的方式傳入getPropertyAsRawString()方法實(shí)現(xiàn)的。
(9)解析PropertyPlaceholderHelper類的replacePlaceholders(String value, PlaceholderResolver placeholderResolver)方法
源碼詳見:org.springframework.util.PropertyPlaceholderHelper#replacePlaceholders(String value, PlaceholderResolver placeholderResolver)。
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, null);
}
可以看到,在PropertyPlaceholderHelper類的replacePlaceholders()方法中,會調(diào)用parseStringValue()方法解析String類型的數(shù)據(jù)。
(10)解析PropertyPlaceholderHelper類的parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable SetvisitedPlaceholders)方法
源碼詳見:org.springframework.util.PropertyPlaceholderHelper#parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable SetvisitedPlaceholders)。
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
/***************省略其他代碼****************/
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
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;
}
}
}
/**********省略其他代碼***********/
}
在PropertyPlaceholderHelper類的parseStringValue()方法中重點(diǎn)關(guān)注如下代碼片段。
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
會調(diào)用placeholderResolver對象的resolvePlaceholder()方法傳入解析 ${xxx.xxx} 占位符,獲取到的key,其中key的形式為xxx.xxx。調(diào)用placeholderResolver對象的resolvePlaceholder()方法會最終調(diào)用PropertySourcesPropertyResolver類的getPropertyAsRawString()方法。
(11)解析PropertySourcesPropertyResolver類的getPropertyAsRawString(String key)方法
源碼詳見:org.springframework.core.env.PropertySourcesPropertyResolver#getPropertyAsRawString(String key)。
@Override
@Nullable
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
可以看到,在getPropertyAsRawString()方法中,會調(diào)用getProperty()方法獲取屬性的值。在調(diào)用getPropertyAsRawString()方法時,傳入的Key的形式的規(guī)則就是:如果使用@Value標(biāo)注的屬性為 ${xxx.xxx} 占位符,則此處傳入的Key的形式為xxx.xxx。
(12)解析PropertySourcesPropertyResolver類的getProperty(String key, ClasstargetValueType, boolean resolveNestedPlaceholders)方法
源碼詳見:org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(String key, ClasstargetValueType, boolean resolveNestedPlaceholders)。
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String string) {
value = resolveNestedPlaceholders(string);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}
在PropertySourcesPropertyResolver類的getProperty()方法中,會從 propertySources 資源中獲取 key = xxx.xxx 的值,如果獲取到一個對應(yīng)的值,就會直接返回。其中,在propertySources中會封裝PropertiesPropertySource、SystemEnvironmentPropertySource和ResourcePropertySource類型的對象,如圖11-8所示。
其中,每種類型的對象中封裝的信息如下所示。
- PropertiesPropertySource:封裝 JVM 環(huán)境變量中的鍵值對。
- SystemEnvironmentPropertySource:封裝操作系統(tǒng)環(huán)境變量中的鍵值對。
- ResourcePropertySource:封裝項目中application.properties、yml和xml等文件中的鍵值對。
通過調(diào)試PropertySourcesPropertyResolver類的getProperty()方法,可以發(fā)現(xiàn),ResourcePropertySource對象中獲取到對應(yīng)的值,如圖11-9所示。
從ResourcePropertySource對象中獲取到對應(yīng)的值就可以設(shè)置到被@Value注解標(biāo)注的字段上。
至此,使用@Value獲取屬性的值的源碼流程分析完畢。
六、總結(jié)
@Value注解介紹完了,我們一起總結(jié)下吧!
本章,詳細(xì)介紹了@Value注解,首先介紹了@Value注解的源碼和使用場景,并且對@Value注解的用法進(jìn)行了簡單的介紹。隨后,介紹了@Value的使用案例。接下來,從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分別詳細(xì)介紹了@Value注解在Spring底層執(zhí)行的源碼時序圖和源碼流程。