spring讀取配置文件原理解析

簡介

  • 在項目中,我們一般都會用配置文件(properties文件)更優雅的實現基礎配置信息;在java中我們一般會用內置的工具包 java.util.Properties去讀取properties配置文件;往往有一些問題是,我們要自己寫代碼去讀取配置文件,還有就是如何管理已經讀取過的配置,這種配置文件的數據一般不會頻繁改變,不可能我需要一次就讀一下文件吧!那就太不優雅了,而且自己去寫讀取文件的代碼也麻煩。
  • Spring剛好能解決這樣的痛點,Spring有一個類PropertyPlaceholderConfigurer,這個類是 BeanFactoryPostProcessor 的實現類。其主要的原理在是。Spring容器初始化的時候,會讀取 xml 或者 annotation 對 Bean 進行初始化。Bean初始化的時候會對配置的 ${xxxx} 進行替換,根據我們Properties文件中配置的進行替換。從而實現表達式的替換操作 。
  • 如果不瞭解BeanFactoryPostProcessor的,可以參考BeanFactoryPostProcessor詳解

使用

我這裏有2中方式,二選一(可能不止)

  • 1、xml方式配置PropertyPlaceholderConfigurer(我源碼演示使用的是第1種)
	......  省略 ............
	  <!-- 加載配置文件 -->
	    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	        <property name="locations">
	            <list>
	                <value>classpath:prop-jdbc.properties</value>
	            </list>
	        </property>
	        
	        <!-- PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver找不到想要的配置而報錯  , 還有個辦法設置忽略ignoreUnresolvablePlaceholders修改爲true -->
	        <property name="ignoreUnresolvablePlaceholders" value="true"></property>
	    </bean>
	    ......  省略 ............
  • 2、java config (看了源碼之後想到直接創建PropertyPlaceholderConfigurer對象);
	package com.zzq.core.configuration;
	
	import java.io.File;
	
	
	import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
	import org.springframework.context.annotation.Bean;
	import org.springframework.context.annotation.Configuration;
	import org.springframework.core.io.FileSystemResource;
	
	
	
	@Configuration
	public class ConfigurationTest    {
	 
		
		//PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver可能找不到想要的配置而報錯  , 還有個辦法設置忽略ignoreUnresolvablePlaceholders修改爲true
		@Bean
		public PropertyPlaceholderConfigurer propertyPlaceholder(){
			PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
			File file = new File("D:/eclipse_workspace/CeShi2/test_web/src/main/resources/zookeeper.properties");
			 
			//File jdbc = new File("D:/eclipse_workspace/CeShi2/test_web/src/main/resources/prop-jdbc.properties");
			 
			org.springframework.core.io.Resource  [] locations = {new FileSystemResource(file)
					/*,new FileSystemResource(jdbc)*/};
			//location.add(new FileSystemResource(file));
			
			configurer.setLocations(locations);
			configurer.setIgnoreUnresolvablePlaceholders(true);
			return configurer;
		} 
	}

要注意此處Resource接口的實現類有很多,我用的絕對路徑FileSystemResource;還可以使用classPath路徑,使用ClassPathResource實現類;還有些其他的可以實現可以自己參考下源碼Resource的其他實現。

來看下prop-jdbc.properties的內容

在這裏插入圖片描述

獲取值可以使用@Value註解,在controller使用

	@Value("${druid.url}")
	private String druidUrl;

最後測試一波,能夠成功獲取到值

在這裏插入圖片描述

源碼分析

基本信息

  • 先來看看PropertyPlaceholderConfigurer的繼承關係
    在這裏插入圖片描述

解析配置文件

  • PropertyResourceConfigurerBeanFactoryPostProcessor的實現類,在項目啓動時會調用到AbstractApplicationContext#invokeBeanFactoryPostProcessors方法,裏面最終就是for循環調用BeanFactoryPostProcessor實現類的postProcessBeanFactory方法;這段邏輯可以參考BeanFactoryPostProcessor詳解
    在這裏插入圖片描述
  • 當調用到PropertyResourceConfigurer#postProcessBeanFactory
	//BeanFactoryPostProcessor的後處理
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			//合併配置文件的屬性值
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			//如有必要, 轉換合併的屬性 
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			//讓子類處理屬性
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}
  • 進入mergeProperties方法
	protected Properties mergeProperties() throws IOException {
		Properties result = new Properties();

		if (this.localOverride) {
			// Load properties from file upfront, to let local properties override.
			loadProperties(result);
		}

		if (this.localProperties != null) {
			for (Properties localProp : this.localProperties) {
				CollectionUtils.mergePropertiesIntoMap(localProp, result);
			}
		}

		if (!this.localOverride) {
			// Load properties from file afterwards, to let those properties override.
			// 從文件加載屬性
			loadProperties(result);
		}

		return result;
	}
  • loadProperties

    	protected void loadProperties(Properties props) throws IOException {
    		if (this.locations != null) {
    			for (Resource location : this.locations) {
    				if (logger.isInfoEnabled()) {
    					logger.info("Loading properties file from " + location);
    				}
    				try {
    					//查找配置文件的屬性 並且都合併到props
    					PropertiesLoaderUtils.fillProperties(
    							props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
    				}
    				catch (IOException ex) {
    					if (this.ignoreResourceNotFound) {
    						if (logger.isWarnEnabled()) {
    							logger.warn("Could not load properties from " + location + ": " + ex.getMessage());
    						}
    					}
    					else {
    						throw ex;
    					}
    				}
    			}
    		}
    	}
    

    在這裏插入圖片描述

    • 驗證我們上面的知識:此時我們知道locationsResource數組,並且實現類是ClassPathResource
  • 來到PropertiesLoaderUtils#fillProperties這個方法就在讀取配置文件了,並把值設置到props

    static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
    			throws IOException {
    
    		InputStream stream = null;
    		Reader reader = null;
    		try {
    			//得到文件名稱
    			String filename = resource.getResource().getFilename();
    			//判斷不爲空  並且後綴是.xml
    			if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
    				stream = resource.getInputStream();
    				persister.loadFromXml(props, stream);
    			}
    			else if (resource.requiresReader()) {
    				// 需要  Reader
    				reader = resource.getReader();
    				persister.load(props, reader);
    			}
    			else {
    				// 普通的properties文件讀取
    				stream = resource.getInputStream();
    				persister.load(props, stream);
    			}
    		}
    		finally {
    			if (stream != null) {
    				stream.close();
    			}
    			if (reader != null) {
    				reader.close();
    			}
    		}
    	}
    

    在這裏插入圖片描述

  • convertProperties轉換屬性沒啥好說的,沒有覆蓋方法,就是返回原值。可以看看最後的convertProperty方法

	protected String convertPropertyValue(String originalValue) {
		return originalValue;
	}
  • 重點是之類覆蓋的方法processProperties,來到子類PropertyPlaceholderConfigurer#processProperties

    	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
    			throws BeansException {
    		// 創建字符串解析器
    		StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    		// 處理屬性 - 重點
    		doProcessProperties(beanFactoryToProcess, valueResolver);
    	}
    
  • doProcessProperties 處理配置屬性

    	protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
    			StringValueResolver valueResolver) {
    
    		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    
    		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
    		for (String curName : beanNames) {
    			// Check that we're not parsing our own bean definition,
    			// to avoid failing on unresolvable placeholders in properties file locations.
    			 //檢查我們沒有解析自己的bean定義,
    		    //以避免在屬性文件位置的不可解析佔位符上失敗。
    			if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
    				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
    				try {
    					visitor.visitBeanDefinition(bd);
    				}
    				catch (Exception ex) {
    					throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
    				}
    			}
    		}
    
    		// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
    		//Spring2.5中的新特性:解析別名目標名稱和別名中的佔位符。
    		beanFactoryToProcess.resolveAliases(valueResolver);
    
    		// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
    		// Spring3.0中的新特性:解析嵌入值(如註釋屬性)中的佔位符。  放到 embeddedValueResolvers屬性裏面 便於註解獲取字符串的值   重點 
    		beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
    	}
    
  • AbstractBeanFactory#addEmbeddedValueResolver

	public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
		Assert.notNull(valueResolver, "StringValueResolver must not be null");
		//  把解析器添加進去
		this.embeddedValueResolvers.add(valueResolver);
	}

設置值

  • 我們首先定位到創建bean的代碼AbstractAutowireCapableBeanFactory#doCreateBean;給屬性賦值,以上面的TestController druidUrl屬性爲例
	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		 .................省略代碼.....................
			 // 這一步也是非常關鍵的,這一步負責屬性裝配,因爲前面的實例只是實例化了,並沒有設值,這裏就是設值
			populateBean(beanName, mbd, instanceWrapper);
	  .................省略代碼.....................
  
	}
  • 再來看看AbstractAutowireCapableBeanFactory#populateBean
	protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
		  // bean 實例的所有屬性都在這裏了
		PropertyValues pvs = mbd.getPropertyValues();

		if (bw == null) {
			if (!pvs.isEmpty()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		// 到這步的時候,bean 實例化完成(通過工廠方法或構造方法),但是還沒開始屬性設值,
		   // InstantiationAwareBeanPostProcessor 的實現類可以在這裏對 bean 進行狀態修改,
		   // 我也沒找到有實際的使用,所以我們暫且忽略這塊吧
		boolean continueWithPropertyPopulation = true;

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					 // 如果返回 false,代表不需要進行後續的屬性設值,也不需要再經過其他的 BeanPostProcessor 的處理
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

		
		
		
		if (!continueWithPropertyPopulation) {
			return;
		}

		//目前看到的上面autowireByName和autowireByType的屬性注入是定義bean(xml定義和代碼定義)的時候纔會進,下面postProcessPropertyValues是用來處理註解的注入  還有@Bean註解可以設置
		
		//mbd.getResolvedAutowireMode()一般爲0 都不進  , 在源碼BeanDefinitionParserDelegate#parseBeanDefinitionAttributes方法發現xml方式可以設置autowireMode屬性值,java config方式目前不知
		//byName 比如可以這樣設置 <bean id ="myTestBean" class= "com.zzq.core.test.entity.MyTestBean" autowire="byName">
		//在mybatis 源碼	ClassPathMapperScanner.class#processBeanDefinitions發現這段代碼直接設置mapper爲byType  if !explicitFactoryUsed  definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			  // 通過名字找到所有屬性值,如果是 bean 依賴,先初始化依賴的 bean。記錄依賴關係
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			 // 通過類型裝配。複雜一些
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}

		//InstantiationAwareBeanPostProcessor接口的主要作用在於目標對象的實例化過程中需要處理的事情,包括實例化對象的前後過程以及實例的屬性設置
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

		if (hasInstAwareBpps || needsDepCheck) {
			//篩選屬性
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						//  此處重點
						 // 這裏有個非常有用的 BeanPostProcessor 進到這裏: AutowiredAnnotationBeanPostProcessor
			               // 對採用 @Autowired、@Value 註解的依賴進行設值,這裏的內容也是非常豐富的,不過本文不會展開說了,感興趣的讀者請自行研究
						//@Resource註解一般使用這個後處理器org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
						//  @Autowired註解是AutowiredAnnotationBeanPostProcessor @Resource註解是CommonAnnotationBeanPostProcessor
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
 
		  // 設置 bean 實例的屬性值,不過pvs空元素的時候並不會進
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
  • 使用@Value註解依賴進行設值的後處理器是AutowiredAnnotationBeanPostProcessor,先來debug看看執行完AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues的效果。
    在這裏插入圖片描述
    在這裏插入圖片描述
    • 此處的pvs是空元素,所以後面執行到applyPropertyValues會直接return。
    • 可以看到在執行完AutowiredAnnotationBeanPostProcessor#postProcessPropertyValuesTestControllerdruidUrl屬性值已經被設置進去了,我們具體看看內部的實現。
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
		// 查找需要依賴注入的元數據屬性
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			// 注入
			metadata.inject(bean, beanName, pvs);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Quick check on the concurrent map first, with minimal locking.
		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 此處是從緩存裏找數據,injectionMetadataCache的元素是在AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors初始化的。buildAutowiringMetadata方法就是封裝一個metadata對象,供下面注入使用。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
		LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
		Class<?> targetClass = clazz;

		do {
			LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
			for (Field field : targetClass.getDeclaredFields()) {
				Annotation ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation is not supported on static fields: " + field);
						}
						continue;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			}
			for (Method method : targetClass.getDeclaredMethods()) {
				Annotation ann = null;
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					ann = findAutowiredAnnotation(bridgedMethod);
				}
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation is not supported on static methods: " + method);
						}
						continue;
					}
					if (method.getParameterTypes().length == 0) {
						if (logger.isWarnEnabled()) {
							logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			}
			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);

		return new InjectionMetadata(clazz, elements);
	}

在這裏插入圖片描述

  • 原理很簡單就是找到滿足條件的加入到list裏,最後構建出一個InjectionMetadata對象返回

  • 現在已經獲取到要注入那些屬性了,現在來看看注入metadata.inject(bean, beanName, pvs);

    	public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
    		 
    		Collection<InjectedElement> elementsToIterate =
    				(this.checkedElements != null ? this.checkedElements : this.injectedElements);
    		if (!elementsToIterate.isEmpty()) {
    			boolean debug = logger.isDebugEnabled();
    			for (InjectedElement element : elementsToIterate) {
    				if (debug) {
    					logger.debug("Processing injected element of bean '" + beanName + "': " + element);
    				}
    				element.inject(target, beanName, pvs);
    			}
    		}
    	}
    

    在這裏插入圖片描述

    • element包裝的就是要注入的屬性,當然element也可能是方法,因爲我看到有兩個實現類;
      在這裏插入圖片描述
  • 現在這兒就看AutowiredFieldElement#inject

    		protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    		Field field = (Field) this.member;
    		try {
    			Object value;
    			if (this.cached) {
    				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    			}
    			else {
    				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    				Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
    				TypeConverter typeConverter = beanFactory.getTypeConverter();
    				//這個beanName 是當前類文件的beanName
    				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    				synchronized (this) {
    					if (!this.cached) {
    						if (value != null || this.required) {
    							this.cachedFieldValue = desc;
    							//保存依賴關係
    							registerDependentBeans(beanName, autowiredBeanNames);
    							if (autowiredBeanNames.size() == 1) {
    								String autowiredBeanName = autowiredBeanNames.iterator().next();
    								if (beanFactory.containsBean(autowiredBeanName)) {
    									if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
    										this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
    									}
    								}
    							}
    						}
    						else {
    							this.cachedFieldValue = null;
    						}
    						this.cached = true;
    					}
    				}
    			}
    			if (value != null) {
    				ReflectionUtils.makeAccessible(field);
    				//通過反射對字段注入
    				field.set(bean, value);
    			}
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException("Could not autowire field: " + field, ex);
    		}
    	}
    
  • 下一步就是獲取屬性值,定位到DefaultListableBeanFactory#resolveDependency

    	public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
    			Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
    		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    		if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
    			return new DependencyObjectFactory(descriptor, beanName);
    		}
    		else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
    			return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
    		}
    		else {
    		     // 一般都是走這裏 
    			return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
    		}
    	}
    
    protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
    		Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
    	//得到註解上的值 一般是獲取value屬性的值
    	Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    	if (value != null) {
    		if (value instanceof String) {
    			// 表達式註解獲取值 一般是獲取配置裏的值  重點
    			String strVal = resolveEmbeddedValue((String) value);
    			BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
    			value = evaluateBeanDefinitionString(strVal, bd);
    		}
    		TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
    		return (descriptor.getField() != null ?
    				converter.convertIfNecessary(value, type, descriptor.getField()) :
    				converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
    	}
    
     ..........省略下面獲取屬性值用不到的代碼................
    	}
    }
    
    • 還記得之前創建的的創建字符串解析器嗎?這裏就會用到它了 resolveEmbeddedValue
    public String resolveEmbeddedValue(String value) {
    		String result = value;
    		for (StringValueResolver resolver : this.embeddedValueResolvers) {
    			if (result == null) {
    				return null;
    			}
    			result = resolver.resolveStringValue(result);
    		}
    		return result;
    	}
    

    在這裏插入圖片描述

    public String resolveStringValue(String strVal) throws BeansException {
    			String value = this.helper.replacePlaceholders(strVal, this.resolver);
    			return (value.equals(nullValue) ? null : value);
    		}
    
    public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
    		Assert.notNull(value, "'value' must not be null");
    		return parseStringValue(value, placeholderResolver, new HashSet<String>());
    	}
    
    	//解析出表達式的值
    protected String parseStringValue(
    		String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
    
    	StringBuilder result = new StringBuilder(strVal);
    	// placeholderPrefix 佔位符前綴
    	int startIndex = strVal.indexOf(this.placeholderPrefix);
    	while (startIndex != -1) {
    		//找到佔位符結束位置的下標
    		int endIndex = findPlaceholderEndIndex(result, startIndex);
    		if (endIndex != -1) {
    			//得到真實的key值
    			String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
    			String originalPlaceholder = placeholder;
    			if (!visitedPlaceholders.add(originalPlaceholder)) {
    				throw new IllegalArgumentException(
    						"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
    			}
    			// Recursive invocation, parsing placeholders contained in the placeholder key.
    			//遞歸調用,分析佔位符鍵中包含的佔位符。
    			placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
    			// Now obtain the value for the fully resolved key...
    			//獲取解析鍵的值
    			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;
    					}
    				}
    			}
    			if (propVal != null) {
    				// Recursive invocation, parsing placeholders contained in the
    				// previously resolved placeholder value.
    				propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
    				//替換佔位符
    				result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
    				if (logger.isTraceEnabled()) {
    					logger.trace("Resolved placeholder '" + placeholder + "'");
    				}
    				startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
    			}
    			else if (this.ignoreUnresolvablePlaceholders) {  //忽略不可解析的佔位符  ,佔位符的解析支持多個StringValueResolver , 因爲某個StringValueResolver可能找不到想要的配置,其他的StringValueResolver能找到,可以設置這個值爲true 避免拋出異常  
    				// Proceed with unprocessed value.
    				// 這裏的startIndex肯定是爲-1的
    				startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
    			}
    			else {
    				// PropertyPlaceholderConfigurer 一般需要把配置文件合併形成 一個 ,否則在AbstractBeanFactory#resolveEmbeddedValue裏面for循環時會有可能遍歷某個StringValueResolver找不到想要的配置而報錯  
    				throw new IllegalArgumentException("Could not resolve placeholder '" +
    						placeholder + "'" + " in string value \"" + strVal + "\"");
    			}
    			visitedPlaceholders.remove(originalPlaceholder);
    		}
    		else {
    			startIndex = -1;
    		}
    	}
    
    	return result.toString();
    }
    
  • 最後會調用到此處PropertyPlaceholderConfigurer#resolvePlaceholder

    	//得到配置文件的值
    	protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
    		String propVal = null;
    		if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
    			propVal = resolveSystemProperty(placeholder);
    		}
    		if (propVal == null) {
    		   //會進入這裏
    			propVal = resolvePlaceholder(placeholder, props);
    		}
    		if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
    			propVal = resolveSystemProperty(placeholder);
    		}
    		return propVal;
    	}
    
    protected String resolvePlaceholder(String placeholder, Properties props) {
    		return props.getProperty(placeholder);
    	}
    
  • 得到了屬性值,最後就是對屬性設值

    在這裏插入圖片描述在這裏插入圖片描述
    在這裏插入圖片描述

  • 本篇文章比較長,讀完確實需要比較大的耐心;最後本人水平有限,如果文章有誤的地方,希望批評指正,感謝您的觀看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章