不难知道, property-placeholder 的解析是 PropertyPlaceholderBeanDefinitionParser 完成的, 但是 它仅仅是个parser , 它仅仅是读取了 location 等配置属性, 并没有完成真正的解析,及 注册。

<context:property-placeholder location="appa.properties"/>

我们把 location 设置为一个错误的 路径,就会报错, 从错误堆栈中可以看出, 实际的解析是 PropertySourcesPlaceholderConfigurer 完成的

警告: 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 more

但是, PropertyPlaceholderBeanDefinitionParser具体是 何时被注册的呢? 我纠结了很久, 仔细调试代码,终于发现了奥秘:

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)

原来是在解析 property-placeholder 这个xml元素的时候完成的, parseCustomElement 。

解析 property-placeholder的时候,找到了 PropertyPlaceholderBeanDefinitionParser 。 然后把 配置中的location 交给了它处理。 

    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 :

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)

具体来说:

    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());
}

spring 之 property-placeholder 分析的更多相关文章

  1. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  2. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  3. spring事务源码分析结合mybatis源码(三)

    下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-s ...

  4. Spring依赖注入原理分析

    在分析原理之前我们先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色( ...

  5. spring整合mybatis步骤分析

    1.spring配置datasource bean的时候,不同的数据库连接方式有有不同的datasource实现类. 比如采用c3p0数据库连接池,要用c3p0的datasource实现类 com.m ...

  6. Spring事务管理全面分析

    Spring 事务属性分析什么是事物  事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常 ...

  7. Spring Boot源码分析-配置文件加载原理

    在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...

  8. 精尽Spring MVC源码分析 - ViewResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  9. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  10. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

随机推荐

  1. 18-09-21 numpy 的基础学习01

    # 1关于numpy 的学习import numpy as np # 一 如何创建数组****# 1 有规律的一维数据的创建======# 1 range() 和arange() 区别 貌似没有区别l ...

  2. python 使用jinjia2 生成文件的空格处理

    例如: <div> {% if True %} yay {% endif %} </div> 生成文件为 <div> yay </div> 两种方法解决 ...

  3. 利用ASK/OOK 发射模块,实现信号重放

    本文以打开无线控制的电动车库卷帘门为目标,深入研究了ASK/OOK的编/解码,并用树莓派+五元钱的ASK/OOK 发射模块 背景 车库装了电动卷帘门,为了了解其安全性,也是为了能自主控制,研究了下其遥 ...

  4. 写了一个Java的简单缓存模型

    缓存操作接口 /** * 缓存操作接口 * * @author xiudong * * @param <T> */ public interface Cache<T> { /* ...

  5. ecmall 后台添加新菜单

    所谓的开发新菜单,其实是和开发模块相对比的,之前说的开发模块,是在应对较大的,或者较为复杂,又相对独立于其他功能的项目需求. 而开发菜单,就是简单的在后台增加一个一级菜单以及其子菜单,或者直接在现有的 ...

  6. 初学java集合框架笔记

    List接口常用方法: 方法名 说    明 boolean add(Object o) 在列表的末尾顺序添加元素, 起始索引位置从0开始 void add(int index,Object o) 在 ...

  7. Visual Studio AI 离线模型训练(Windows10)

    一.序 环境搭建:[查看] samples-for-ai项目下载:[下载],两个版本,一个2018年6月15日前,一个2018年6月15日-16日版本(当前最新版本). 在环境搭建过程中,通过git ...

  8. "Error: [$compile:multidir] Multiple directives

    angularjs 中控制台报"Error: [$compile:multidir] Multiple directives错误 可以在当前html元素之上增加父元素,在父元素中加入第二个指 ...

  9. arm-linux-ld:u-boot.lds:1: ignoring invalid character `#' in expression

    在裁剪uboot的时候出现下面错误: LDS u-boot.lds LD u-boot arm-linux-: ignoring invalid character `#' in expression ...

  10. 一篇关于CountDownLatch的好文章

    CountDownLatch简介 CountDownLatch是一种java.util.concurrent包下一个同步工具类,它允许一个或多个线程等待直到在其他线程操作执行完成. 使用场景: 在开发 ...