不难知道, 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. jmeter+jdk 环境搭建

    1.安装jdk环境  https://jingyan.baidu.com/article/6dad5075d1dc40a123e36ea3.html 划上重点,,,如果发现java -version不 ...

  2. python笔记17-全局变量、局部变量

    在函数里面定义变量叫局部变量,它只能在函数里面用出了该函数外,就不能使用了在函数外面定义的变量,是全局变量,在函数内也可以使用 如果想在函数里面修改全局变量的值,那么要先用global关键字声明 要修 ...

  3. hadoop Non DFS Used是什么

    首先我们先来了解一下Non DFS User是什么? Non DFS User的意思是:非hadoop文件系统所使用的空间,比如说本身的linux系统使用的,或者存放的其它文件   它的计算公式: n ...

  4. mybatis oracle:批量操作(增删改查)

    此文主要是讲mybatis在连接oracle数据库时的一些批量操作,请各位对号入座 (最后回来补充一下,所有都是在Spring+MVC的框架下实现的) 不废话,上代码: 1.批量插入(网上很多,是针对 ...

  5. Linux下的5种I/O模型(转)

    Linux下的五种I/O模型: l         阻塞I/O l         非阻塞I/O l         I/O复用(select.poll.epoll) l         信号驱动I/ ...

  6. Linux每天一个命令:nc/ncat

    nmap-ncat.x86_64版nc/ncat nc/ncat所做的就是在两台电脑之间建立链接并返回两个数据流,在这之后所能做的事就看你的想像力了.你能建立一个服务器,传输文件,与朋友聊天,传输流媒 ...

  7. 马凯军201771010116《面向对象程序设计(java)》第四周学习总结

    第一部分:理论知识学习部分 第四章 1.类与对象的基础概念. 对象:即数据,对象有三个特性:行为 .状态.标识. 类是对象,事物的描述和抽象,是具有相同属性和行为的对象集合.对象则是该类事物的实例. ...

  8. 百战程序员-xml

    1.用自己的语言说出,为什么需要XML? XML 是一种元标注语言,该语言提供一种描述结构数据的格式.这有助于更精确地声明内容,方便跨越多种平台的更有意义的搜索结果.此外,XML 将起用新一代的基于 ...

  9. 在linux中编译grpc

    环境: centos_7_x86_x64 一.下载 1)下载grpc源代码:grpc-1.2.0.zip 2)下载grpc依赖库: 1)benchmark-master.zip 2)boringssl ...

  10. zabbix与tomcat(六)

    一.zabbix监控远程tomcat的流程   Zabbix-server 找 zabbix Java Gateway获取Java数据 zabbix Java Gateway 找Java程序(zabb ...