不難知道, property-placeholder 的解析是 PropertyPlaceholderBeanDefinitionParser 完成的, 可是 它僅僅是個parser , 它僅僅是讀取了 location 等配置屬性, 並無完成真正的解析,及 註冊。java
<context:property-placeholder location="appa.properties"/>
咱們把 location 設置爲一個錯誤的 路徑,就會報錯, 從錯誤堆棧中能夠看出, 實際的解析是 PropertySourcesPlaceholderConfigurer 完成的: spring
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [appa.properties] cannot be opened because it does not exist
Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [appa.properties] cannot be opened because it does not exist
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:148)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:281)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:161)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at LKtest.main(LKtest.java:11)
Caused by: java.io.FileNotFoundException: class path resource [appa.properties] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)
at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:154)
at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:98)
at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:158)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:139)
... 7 moreapp
可是, PropertyPlaceholderBeanDefinitionParser具體是 什麼時候被註冊的呢? 我糾結了好久, 仔細調試代碼,終於發現了奧祕:ide
at org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser.getBeanClass(PropertyPlaceholderBeanDefinitionParser.java:48)
at org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser.parseInternal(AbstractSingleBeanDefinitionParser.java:66) // 又交給了實際的子類去parse
at org.springframework.beans.factory.xml.AbstractBeanDefinitionParser.parse(AbstractBeanDefinitionParser.java:61)
at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:74)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1411)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1401)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:172)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:142)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:94)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:252)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:613)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:514)
- locked <0x5d2> (a java.lang.Object)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at LKtest.main(LKtest.java:11)post
原來是在解析 property-placeholder 這個xml元素的時候完成的, parseCustomElement 。ui
解析 property-placeholder的時候,找到了 PropertyPlaceholderBeanDefinitionParser 。 而後把 配置中的location 交給了它處理。 this
AbstractSingleBeanDefinitionParser: protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = this.getParentName(element); if(parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } Class<?> beanClass = this.getBeanClass(element); 根據context:property-placeholder, 找到class。返回的正是 PropertySourcesPlaceholderConfigurer .. } PropertyPlaceholderBeanDefinitionParser: protected Class<?> getBeanClass(Element element) { return "ENVIRONMENT".equals(element.getAttribute("system-properties-mode"))?PropertySourcesPlaceholderConfigurer.class:PropertyPlaceholderConfigurer.class; }
可是我仍是不知道 properties 文件是如何被讀取和解析的, 直到我注意到 PropertySourcesPlaceholderConfigurer 實現了 BeanFactoryPostProcessor :spa
at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:85)
at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177) // 這裏完成了properties 的讀取
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:158)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:139) // 由於PropertySourcesPlaceholderConfigurer 實現了 BeanFactoryPostProcessor
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:281)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:161)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524)
- locked <0x6c6> (a java.lang.Object)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at LKtest.main(LKtest.java:11)
調試
具體來講:code
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if(this.propertySources == null) { this.propertySources = new MutablePropertySources(); if(this.environment != null) { this.propertySources.addLast(new PropertySource<Environment>("environmentProperties", this.environment) { public String getProperty(String key) { return ((Environment)this.source).getProperty(key); } }); } try { PropertySource<?> localPropertySource = new PropertiesPropertySource("localProperties", this.mergeProperties()); // BeanFactory 建立完成後, 讀取全部的 properties 文件 if(this.localOverride) { this.propertySources.addFirst(localPropertySource); } else { this.propertySources.addLast(localPropertySource); } } catch (IOException var3) { throw new BeanInitializationException("Could not load properties", var3); } } this.processProperties(beanFactory, (ConfigurablePropertyResolver)(new PropertySourcesPropertyResolver(this.propertySources)));// 將properties 文件內容應用到全部的配置中去,也就是替換 ${} 部分 this.appliedPropertySources = this.propertySources; } 對 ${} 的替換: at org.springframework.beans.factory.config.BeanDefinitionVisitor.visitPropertyValues(BeanDefinitionVisitor.java:142) at org.springframework.beans.factory.config.BeanDefinitionVisitor.visitBeanDefinition(BeanDefinitionVisitor.java:82) at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:220) // 完成了全部的beanDefinition 的預處理以後, 開始替換 Placeholder at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:180) at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:152) ... protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); // 獲取全部已經註冊的 bean 定義 String[] var5 = beanNames; int var6 = beanNames.length; for(int var7 = 0; var7 < var6; ++var7) { String curName = var5[var7]; if(!curName.equals(this.beanName) || !beanFactoryToProcess.equals(this.beanFactory)) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { visitor.visitBeanDefinition(bd); } catch (Exception var11) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, var11.getMessage(), var11); } } } beanFactoryToProcess.resolveAliases(valueResolver); beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); } PropertySourcesPlaceholderConfigurer的父類定義了 如何設置 placeholder, 可見前綴和後綴: public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware { public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; public static final String DEFAULT_VALUE_SEPARATOR = ":"; protected String placeholderPrefix = "${"; protected String placeholderSuffix = "}"; protected String valueSeparator = ":"; PropertySource 就是一個kv 配置項。 其中位於 BeanDefinitionVisitor : public void visitBeanDefinition(BeanDefinition beanDefinition) { this.visitParentName(beanDefinition); this.visitBeanClassName(beanDefinition); this.visitFactoryBeanName(beanDefinition); this.visitFactoryMethodName(beanDefinition); this.visitScope(beanDefinition); this.visitPropertyValues(beanDefinition.getPropertyValues()); ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); this.visitIndexedArgumentValues(cas.getIndexedArgumentValues()); this.visitGenericArgumentValues(cas.getGenericArgumentValues()); }