spring 之 property-placeholder 分析
不难知道, 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 分析的更多相关文章
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
- Spring Ioc源码分析系列--Ioc源码入口分析
Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...
- spring事务源码分析结合mybatis源码(三)
下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-s ...
- Spring依赖注入原理分析
在分析原理之前我们先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色( ...
- spring整合mybatis步骤分析
1.spring配置datasource bean的时候,不同的数据库连接方式有有不同的datasource实现类. 比如采用c3p0数据库连接池,要用c3p0的datasource实现类 com.m ...
- Spring事务管理全面分析
Spring 事务属性分析什么是事物 事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常 ...
- Spring Boot源码分析-配置文件加载原理
在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...
- 精尽Spring MVC源码分析 - ViewResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
随机推荐
- python笔记16-函数
函数说白了,就是把一组代码合到一起,可以实现某种功能,需要再用到这个功能的话,直接调用这个函数就行了 1.定义函数def def my_open():#函数名,def定义函数,my_open给这个函数 ...
- 基于Verilog的简单FIFO读写实验
一.模块框图及基本思路 fifo_ip:ISE生成的IP fifo_control:在fifo未满情况下不断写入递增的四位数,每隔1s读出一个数据驱动Led显示 fifo_top:前两个模块的组合 二 ...
- linux中运行.sql文件
1.linux目录结构 2.假设home下面有a.sql文件 先得use databasename,要不会报错 “No Database Selected” 然后source /home/a.sql ...
- 虚拟机U盘挂载
虚拟机中U盘挂载 一.连接U盘 虚拟机中 虚拟机→可移动设备→Syntek USB......(U盘的名称)→连接: 二.查看U盘的UUID “lsblk -f”: UUID为 35E6-9 ...
- 剑指Offer 33. 丑数 (其他)
题目描述 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 题目地 ...
- vue-router(配置子路由--单页面多路由区域操作)
1.配置子路由: import Post from "@components/Post" export default new Router({ routers:[ { path: ...
- git教程: 查看文件状态与修改内容
转载:时光机穿梭 我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容: Git is a distributed ...
- tofile和fromfile数组内建函数
numpy教程:基本输入输出和文件输入输出Input and output 使用数组的方法函数tofile可以方便地将数组中数据以二进制的格式写进文件.tofile输出的数据没有格式,因此用numpy ...
- jquery常用指令
table中td多行展示: ```css td { word-wrap: break-word; } ``` div模态框: <div id="loading" style= ...
- addEventListener以及滑轮滑动事件的应用
addEventListener用于向元素添加事件,而其适用于较新版的IE浏览器(如IE9),对于IE6/7/8来说,应该用attachEvent 下面的代码即为向<img>元素添加事件 ...