深度解析@Value注解,你真的徹底了解過嗎?
一、學習指引
Spring中的@Value注解,你真的徹底了解過嗎?
在實際開發過程中,通常會有這樣一種場景:將一些配置項寫到配置文件中,在業務邏輯中會讀取配置文件中的配置項,取出對應的值進行業務邏輯處理。Spring中提供的@Value注解就可以讀取配置文件中的值。另外@Value注解也可以向Bean中的屬性設置其他值。本章,就對@Value注解進行簡單的介紹。
二、注解說明
關于@Value注解的一點點說明~~
@Value注解可以向Spring的Bean的屬性中注入數據。并且支持Spring的EL表達式,可以通過${} 的方式獲取配置文件中的數據。配置文件支持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注解可以標注到字段、方法、參數和其他注解上,@Value注解中提供了一個String類型的value屬性,具體含義如下所示。
- value:指定要向Bean的屬性中注入的數據,數據可以是配置文件中的配置項,并且支持EL表達式。
2、使用場景
在實際開發中,項目中難免會有一些配置信息,此時,就可以將這些配置信息統一寫到配置文件中。隨后使用@Value注解讀取配置文件的值來向Spring中Bean的屬性設置值。
例如,一些系統環境變量信息,數據庫配置,系統通用配置等等,都可以保存到配置文件中,此時就可以使用Spring的EL表達式讀取配置文件中的值。
3、用法
本節,主要介紹不通過配置文件注入屬性和通過配置文件注入屬性兩種情況來介紹@Value注解的用法。
不通過配置文件注入屬性
通過@Value可以將外部的值動態注入到Bean中,有如下幾種用法。
(1)注入普通字符串。
@Value("normalString")
private String normalString;
(2)注入操作系統屬性。
@Value("#{systemProperties['os.name']}")
private String osName;
(3)注入表達式的結果信息。
@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中#{...}和${...}的區別
這里提供一個測試屬性文件:test.properties,大致的內容如下所示。
server.name=server1,server2,server3
author.name=binghe
測試類Test:引入test.properties文件,作為屬性的注入。
@Component
@PropertySource({"classpath:test.properties"})
public class Test {
}
${...}的用法
{}里面的內容必須符合SpEL表達式, 通過@Value(“${spelDefault.value}”)可以獲取屬性文件中對應的值,但是如果屬性文件中沒有這個屬性,則會報錯。可以通過賦予默認值解決這個問題,如下所示。
@Value("${author.name:binghe}")
上述代碼的含義表示向Bean的屬性中注入配置文件中的author.name屬性的值,如果配置文件中沒有author.name屬性,則向Bean的屬性中注入默認值binghe。例如下面的代碼片段。
@Value("${author.name:binghe}")
private String name;
#{…}的用法
(1)SpEL:調用字符串Hello World的concat方法
@Value("#{'Hello World'.concat('!')}")
private String helloWorld;
(2)SpEL: 調用字符串的getBytes方法,然后調用length屬性
@Value("#{'Hello World'.bytes.length}")
private int length;
${…}和#{…}混合使用
${...}和#{...}可以混合使用,如下文代碼執行順序:傳入一個字符串,根據 "," 切分后插入列表中, #{}和${}配合使用,注意單引號。
@Value("#{'${server.name}'.split(',')}")
private List<String> servers;
注意:${}和#{}混合實用時,不能${}在外面,#{}在里面。因為Spring執行${}的時機要早于#{},當Spring執行外層的${}時,內部的#{}為空,會執行失敗。
@Value注解用法總結
- #{…} 用于執行SpEl表達式,并將內容賦值給屬性。
- ${…} 主要用于加載外部屬性文件中的值。
- #{…} 和${…} 可以混合使用,但是必須#{}外面,${}在里面。
三、使用案例
@Value的實現案例,我們一起實現吧~~
本節,就基于@Value注解實現向Bean屬性中賦值的案例,具體的實現步驟如下所示。
1、新增test.properties配置文件
在spring-annotation-chapter-11工程下的resources目錄下新增test.properties配置文件,內容如下所示。
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類上標注了@Component注解,說明當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;
/**
* 注入操作系統名稱
*/
@Value("#{systemProperties['os.name']}")
private String osName;
/**
* 注入表達式的結果
*/
@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類上標注了@Configuration注解,說明ValueConfig類是Spring的配置類。使用@ComponentScan注解指定了掃描的包名是io.binghe.spring.annotation.chapter11。并且使用@PropertySource注解導入了test.properties配置文件。ValueConfig類的字段通過@Value注解注入對應的屬性值,代碼中有詳細的注釋,這里不再贅述。
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類是案例程序的測試類,實現的代碼比較簡單,這里不再贅述。
5、運行ValueTest類
運行ValueTest類的main()方法,輸出的結果信息如下所示。
ValueConfig{normalString='normalString', osName='Windows 10', randomNum=60.704013358598715, name='binghe', dbUrl='jdbc:mysql://localhost:3306/test'}
可以看到,在ValueTest類中的各個字段值都輸出了正確的結果數據。
說明:使用@Value注解向Bean的屬性中正確設置了值。
四、源碼時序圖
結合時序圖理解源碼會事半功倍,你覺得呢?
本節,就以源碼時序圖的方式,直觀的感受下@Value注解在Spring源碼層面的執行流程。本節,會從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分析源碼時序圖。
注意:本節以單例Bean為例分析源碼時序圖,并且基于@Value注解標注到類的字段上的源碼時序圖為例進行分析,@Value注解標注到類的方法上的源碼時序圖與標注到字段上的源碼時序圖基本相同,不再贅述。
1、解析并獲取@Value修飾的屬性
本節,就簡單介紹下解析并獲取@Value修飾的屬性的源碼時序圖,整體如圖11-1~11-2所示。
由圖11-1~11-2可以看出,解析并獲取@Value修飾的屬性的流程中涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類和AutowiredAnnotationBeanPostProcessor類。具體的源碼執行細節參見源碼解析部分。
2、為@Value修飾的屬性賦值
本節,就簡單介紹下為@Value修飾的屬性賦值的源碼時序圖,整體如圖11-3~11-4所示。
由圖11-3~11-4所示,為@Value修飾的屬性賦值流程涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類、AutowiredAnnotationBeanPostProcessor類、InjectionMetadata類和AutowiredFieldElement類。具體的源碼執行細節參見源碼解析部分。
3、使用@Value獲取屬性的值
本節,就簡單介紹下使用@Value注解獲取屬性的值的源碼時序圖,整體如圖11-5~11-7所示。
由圖11-5~11-7所示,使用@Value獲取屬性的值的流程涉及到ValueTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、DefaultListableBeanFactory類、AbstractBeanFactory類、AbstractAutowireCapableBeanFactory類、AutowiredAnnotationBeanPostProcessor類、InjectionMetadata類、AutowiredFieldElement類、AbstractEnvironment類、AbstractPropertyResolver類、PropertyPlaceholderHelper類和PropertySourcesPropertyResolver類。具體的源碼執行細節參見源碼解析部分。
五、源碼解析
源碼時序圖整清楚了,那就整源碼解析唄!
本節,主要分析@Value注解在Spring源碼層面的執行流程,同樣的,本節也會從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分析源碼執行流程,并且結合源碼執行的時序圖,會理解的更加深刻。
注意:本節以單例Bean為例分析,并且基于@Value注解標注到類的字段上的源碼流程為例進行分析,@Value注解標注到類的方法上的源碼流程與標注到字段上的源碼流程基本相同,不再贅述。
1、解析并獲取@Value修飾的屬性
本節主要對解析并獲取 @Value 修飾屬性的源碼流程進行簡單的分析,結合源碼執行的時序圖,會理解的更加深刻,本節的源碼執行流程可以結合圖11-1~11-2進行理解。具體分析步驟如下所示。
注意:解析并獲取 @Value 修飾屬性源碼流程的前半部分與第7章5.3節分析源碼的流程相同,這里,從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)。重點關注如下代碼片段。
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,會調用getSingleton()方法創建單例Bean,實際執行的是Lambda表達式中的createBean()方法來創建單例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()方法中,會調用doCreateBean()方法創建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)。此時重點關注創建Bean實例的代碼片段,如下所示。
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()方法會創建Bean的實例并返回BeanWrapper對象。
(5)返回AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法,此時,重點關注如下代碼片段。
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()方法中會調用applyMergedBeanDefinitionPostProcessors()方法的主要作用就是:獲取@Value、@Autowired、@PostConstruct、@PreDestroy等注解標注的字段和方法,然后封裝到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()方法中,會調用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()方法中會調用findInjectionMetadata()方法來獲取標注了注解的字段或者方法。
(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()方法中,調用了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()方法需要重點關注下,findAutowiringMetadata()方法最核心的功能就是對傳遞進來的每個類進行篩選判斷是否被@Value、@Autowired、@Inject注解修飾的方法或者屬性,如果是被 @Value、@Autowired、@Inject注解修飾的方法或者屬性,就會將這個類記錄下來,存入injectionMetadataCache緩存中,為后續的 DI 依賴注作準備。
首次調用findAutowiringMetadata()方法時,會調用buildAutowiringMetadata()方法來查找使用@Value、@Autowired、@Inject注解修飾的方法或者屬性。
(10)解析AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata(Class<?> clazz)方法。
源碼詳見:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata(Class<?> clazz)。方法中會查找@Value、@Autowired、@Inject注解修飾的方法或者屬性,這里以查找屬性為例,重點關注如下代碼片段。
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()方法中,獲取到類上所有的字段,然后遍歷每個字段,判斷是否標注了 @Value、@Autowired和@Inject注解,如果標注了 @Value、@Autowired和@Inject注解,直接封裝成 AutowiredFieldElement 對象,然后保存到一個名為 currElements集合中。
指的一提的是,如果解析到的字段是靜態字段,則直接返回,這就是為什么Spring不會對類中的靜態字段賦值的原因。如下代碼片段所示。
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
在AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata()方法的最后,則將標注了@Value、@Autowired和@Inject注解的字段封裝到 InjectionMetadata 對象中,如下所示。
return InjectionMetadata.forElements(elements, clazz);
最終回到AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata()方法中,將InjectionMetadata 對象存入injectionMetadataCache緩存中。如下所示。
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
另外,在AutowiredAnnotationBeanPostProcessor類的buildAutowiringMetadata()方法中,調用了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。
這里,需要關注下autowiredAnnotationTypes集合,在AutowiredAnnotationBeanPostProcessor類的構造方法中向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類的構造方法中,向autowiredAnnotationTypes集合中添加了@Autowired注解、@Value注解和@Inject注解。所以,@Autowired注解賦值的流程和@Value注解賦值的流程基本一致。
至此,解析并獲取@Value注解修飾的屬性的源碼流程分析完畢。
2、為@Value修飾的屬性賦值
本節主要對為@Value修飾的屬性賦值的源碼流程進行簡單的分析,結合源碼執行的時序圖,會理解的更加深刻,本節的源碼執行流程可以結合圖11-3~11-4進行理解。具體分析步驟如下所示。
注意:為@Value修飾的屬性賦值的源碼流程的前半部分與本章5.1節分析源碼的流程相同,這里,同樣從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)。重點關注如下代碼片段。
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()方法中,會調用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()方法中,會調用InstantiationAwareBeanPostProcessor類的postProcessProperties()方法來處理屬性或方法的值。實際上是調用的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()方法中,會調用findAutowiringMetadata()方法獲取注解的元數據信息。
(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修飾的屬性的代碼流程中,已經完成了對@Value 修飾的屬性的獲取工作。所以,程序執行到findAutowiringMetadata()方法內部時,injectionMetadataCache緩存中已經有數據了。
(5)返回AutowiredAnnotationBeanPostProcessor類的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法。在AutowiredAnnotationBeanPostProcessor類的postProcessProperties()方法中,調用了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()方法中,會循環遍歷checkedElements集合,調用遍歷出的每個InjectedElement對象的inject()方法為屬性賦值。
注意:調用InjectedElement對象的inject()方法時,實際上可能會調用AutowiredFieldElement類的inject()方法、AutowiredMethodElement類的inject()方法或者InjectedElement類的inject()方法。這里,以調用AutowiredFieldElement類的inject()方法為例進行說明。
(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()方法中,會調用resolveFieldValue()方法來獲取對應的屬性值,如下所示。
value = resolveFieldValue(field, bean, beanName);
并通過反射向使用@Value注解標注的字段賦值,如下所示。
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的屬性賦值后會調用initializeBean()方法對Bean進行初始化。
(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()方法中,會調用applyBeanPostProcessorsBeforeInitialization()方法在初始化之前執行一些邏輯,然后調用invokeInitMethods()執行真正的初始化操作,執行完Bean的初始化,會調用applyBeanPostProcessorsAfterInitialization()方法執行初始化之后的一些邏輯。
至此,為@Value修飾的屬性賦值的源碼流程分析完畢。
3、使用@Value獲取屬性的值
本節主要對使用@Value獲取屬性的值的源碼流程進行簡單的分析,結合源碼執行的時序圖,會理解的更加深刻,本節的源碼執行流程可以結合圖11-5~11-7進行理解。具體分析步驟如下所示。
注意:使用@Value獲取屬性的值的源碼流程的前半部分與本章5.2節分析源碼的流程相同,這里,從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()方法中,會調用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()方法中,會調用beanFactory對象的resolveDependency()方法,繼續向下分析。
(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()方法中,會調用doResolveDependency()方法進一步處理。
(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()方法中,如果當前獲取到的數據是String類型,則調用resolveEmbeddedValue()方法進行處理。
(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()中,會調用遍歷出來的StringValueResolver對象的resolveStringValue()方法進行處理。此時,會進入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()方法中,會調用propertyResolver對象的resolvePlaceholders()方法進行處理。
(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()方法中,會調用doResolvePlaceholders()方法進一步處理。
(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,隨后根據key去資源文件(xml、application.properties、Environment 等)中查找是否配置了這個key的值。其實是通過調用helper的replacePlaceholders()方法并以Lambda表達式的方式傳入getPropertyAsRawString()方法實現的。
(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()方法中,會調用parseStringValue()方法解析String類型的數據。
(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()方法中重點關注如下代碼片段。
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
會調用placeholderResolver對象的resolvePlaceholder()方法傳入解析 ${xxx.xxx} 占位符,獲取到的key,其中key的形式為xxx.xxx。調用placeholderResolver對象的resolvePlaceholder()方法會最終調用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()方法中,會調用getProperty()方法獲取屬性的值。在調用getPropertyAsRawString()方法時,傳入的Key的形式的規則就是:如果使用@Value標注的屬性為 ${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 的值,如果獲取到一個對應的值,就會直接返回。其中,在propertySources中會封裝PropertiesPropertySource、SystemEnvironmentPropertySource和ResourcePropertySource類型的對象,如圖11-8所示。
其中,每種類型的對象中封裝的信息如下所示。
- PropertiesPropertySource:封裝 JVM 環境變量中的鍵值對。
- SystemEnvironmentPropertySource:封裝操作系統環境變量中的鍵值對。
- ResourcePropertySource:封裝項目中application.properties、yml和xml等文件中的鍵值對。
通過調試PropertySourcesPropertyResolver類的getProperty()方法,可以發現,ResourcePropertySource對象中獲取到對應的值,如圖11-9所示。
從ResourcePropertySource對象中獲取到對應的值就可以設置到被@Value注解標注的字段上。
至此,使用@Value獲取屬性的值的源碼流程分析完畢。
六、總結
@Value注解介紹完了,我們一起總結下吧!
本章,詳細介紹了@Value注解,首先介紹了@Value注解的源碼和使用場景,并且對@Value注解的用法進行了簡單的介紹。隨后,介紹了@Value的使用案例。接下來,從解析并獲取 @Value 修飾的屬性、為 @Value 修飾屬性賦值和使用@Value獲取屬性值三個方面分別詳細介紹了@Value注解在Spring底層執行的源碼時序圖和源碼流程。