Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载
转:
Spring中property-placeholder的使用与解析
Spring中property-placeholder的使用与解析
我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中.
代码分析的时候,涉及的知识点概要:
- NamespaceHandler 解析xml配置文件中的自定义命名空间
- ContextNamespaceHandler 上下文相关的解析器,这边定义了具体如何解析property-placeholder的解析器
- BeanDefinitionParser 解析bean definition的接口
- BeanFactoryPostProcessor 加载好bean definition后可以对其进行修改
- PropertySourcesPlaceholderConfigurer 处理bean definition 中的占位符
我们先来看看具体的使用吧
property的使用
在xml文件中配置properties文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:property-placeholder location="classpath:foo.properties" />
</beans>
这样/src/main/resources/foo.properties文件就会被spring加载
如果想使用多个配置文件,可以添加order字段来进行排序
使用PropertySource注解配置
Spring3.1添加了@PropertySource注解,方便添加property文件到环境.
@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
properties的注入与使用
- java中使用@Value注解获取
@Value( "${jdbc.url}" )
private String jdbcUrl;
还可以添加一个默认值
@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
- 在Spring的xml配置文件中获取
<bean id="dataSource">
<property name="url" value="${jdbc.url}" />
</bean>
源码解析
properties配置信息的加载
Spring在启动时会通过AbstractApplicationContext#refresh启动容器初始化工作,期间会委托loadBeanDefinitions解析xml配置文件.
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
loadBeanDefinitions通过层层委托,找到DefaultBeanDefinitionDocumentReader#parseBeanDefinition解析具体的bean
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
这边由于不是标准类定义,所以委托BeanDefinitionParserDelegate解析
通过NamespaceHandler查找到对应的处理器是ContextNamespaceHandler,再通过id找到PropertyPlaceholderBeanDefinitionParser解析器解析
@Override
public void init() {
// 这就是我们要找的解析器
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
PropertyPlaceholderBeanDefinitionParser是这一轮代码分析的重点.
我们来看看它的父类吧.
- BeanDefinitionParser
被DefaultBeanDefinitionDocumentReader用于解析个性化的标签
这边只定义了一个解析Element的parse api
public interface BeanDefinitionParser {
BeanDefinition parse(Element element, ParserContext parserContext);
}
- AbstractBeanDefinitionParser
BeanDefinitionParser接口的默认抽象实现.spring的拿手好戏,这边提供了很多方便使用的api,并使用模板方法设计模式给子类提供自定义实现的钩子
我们来看看parse时具体的处理逻辑把:- 调用钩子parseInternal解析
- 生成bean id,使用BeanNameGenerator生成,或者直接读取id属性
- 处理name 与别名aliases
- 往容器中注册bean
- 进行事件触发
AbstractSingleBeanDefinitionParser
解析,定义单个BeanDefinition的抽象父类
在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder进行封装AbstractPropertyLoadingBeanDefinitionParser
解析property相关的属性,如location,properties-ref,file-encoding,order等PropertyPlaceholderBeanDefinitionParser
这边处理的事情不多,就是设置ingore-unresolvable和system-properties-mode
properties文件的加载,bean的实例化
接下来,我们再看看这个bean是在什么时候实例化的,一般类的实例化有2种,一种是单例系统启动就实例化;一种是非单例(或者单例懒加载)在getBean时实例化.
这边的触发却是通过BeanFcatoryPostProcessor.
BeanFactoryPostProcessor是在bean实例化前,修改bean definition的,比如bean definition中的占位符就是这边解决的,而我们现在使用的properties也是这边解决的.
这个是通过PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors实现的.
扫描容器中的BeanFactoryPostProcessor,找到了这边需要的PropertySourcesPlaceholderConfigurer,并通过容器的getBean实例化
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
PropertySourcesPlaceholderConfigurer实例化完成后,就直接进行触发,并加载信息
OrderComparator.sort(priorityOrderedPostProcessors);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
我们再来看看PropertySourcesPlaceholderConfigurer的继承体系把
BeanFactoryPostProcessor
定义一个用于修改容器中bean definition的属性的接口.其实现类在一般类使用前先实例化,并对其他类的属性进行修改.
这跟BeanPostProcessor有明显的区别,BeanPostProcessor是修改bean实例的.PropertiesLoaderSupport
加载properties文件的抽象类.
这边具体的加载逻辑是委托PropertiesLoaderUtils#fillProperties实现PropertyResourceConfigurer
bean definition中占位符的替换就是这个抽象类实现的.
实现BeanFactoryPostProcessor#postProcessBeanFactory,迭代容器的中的类定义,进行修改
具体如何修改就通过钩子processProperties交由子类实现PlaceholderConfigurerSupport
使用visitor设计模式,通过BeanDefinitionVisitor和StringValueResolver更新属性
StringValueResolver是一个转化String类型数据的接口,真正更新属性的api实现竟然是在PropertyPlaceholderHelper#parseStringValuePropertySourcesPlaceholderConfigurer
覆写postProcessorBeanFactory api定义解析流程
Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载的更多相关文章
- spring中 context:property-placeholder 导入多个独立的 .properties配置文件
spring中 context:property-placeholder 导入多个独立的 .properties配置文件? Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个 o ...
- spring中context:property-placeholder/元素 转载
spring中context:property-placeholder/元素 转载 1.有些参数在某些阶段中是常量 比如 :a.在开发阶段我们连接数据库时的连接url,username,passwo ...
- Spring中<context:annotation-config/>
最近在研究Spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Sp ...
- Spring中<context:annotation-config/>的作用
spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Spring容 ...
- 第5章—构建Spring Web应用程序—关于spring中的validate注解后台校验的解析
关于spring中的validate注解后台校验的解析 在后台开发过程中,对参数的校验成为开发环境不可缺少的一个环节.比如参数不能为null,email那么必须符合email的格式,如果手动进行if判 ...
- 【Spring MVC】Properties文件的加载
[Spring MVC]Properties文件的加载 转载:https://www.cnblogs.com/yangchongxing/p/10726885.html 参考:https://java ...
- web.xml中如何设置配置文件的加载路径
web应用程序通过Tomcat等容器启动时,会首先加载web.xml文件,通常我们工程中的各种配置文件,如日志.数据库.spring的文件等都在此时被加载,下面是两种常用的配置文件加载路径,即配置文件 ...
- 用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机
原文:用MVVM模式开发中遇到的零散问题总结(5)--将动态加载的可视元素保存为图片的控件,Binding刷新的时机 在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打 ...
- [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型
[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型 作者:u0u0 - iTyran 在上一节中,我们分析了OBJ格式.OBJ格式优点是文本形式,可读 ...
随机推荐
- EEPROM---AT24Cxx应用介绍
结论:1.读写AT24CXX芯片,根据容量有多种方式:一.容量为AT24C01~AT24C16,首先发送设备地址(8位地址),再发送数据地址(8位地址),再发送或者接受数据. 二.AT24C32/AT ...
- Eclipse中Hadoop插件配置
Eclipse中Hadoop插件DFS配置 http://www.cnblogs.com/xia520pi/archive/2012/05/20/2510723.html
- Linux每天一个命令:cat
Linux cat命令 命令:cat cat 命令用于连接文件并打印到标准输出设备上. 使用权限 所有使用者 语法格式 cat [-AbeEnstTuv] [--help] [--version] f ...
- mybatis 异常 too many connections 解决方案 mysql
参考: https://blog.csdn.net/u011628250/article/details/54017481 https://www.cnblogs.com/baby123/p/5710 ...
- SSIS 遍历目录,把文件内容导入数据库
最近接手一个项目,程序的基本框架是:程序A导出数据,以.tsv格式存储,数据列之间以tab间隔:程序B吃文档,把数据导入到数据库中,并把处理过的文档备份/移动到指定的目录中.为了快速开发,程序B设计成 ...
- SpringMVC源码总结
SpringMVC源码总结 http://blog.csdn.net/z69183787/article/details/52816927
- kubernetes部署mysql
第一章 部署K8S集群 https://www.cnblogs.com/zoulixiang/p/9504324.html 第二章 1.新建mysql-rc.yaml vi mysql-rc.yaml ...
- mysql学习(3)10045错误,连接不上数据库
mysql8.0默认加密的方式是caching_sha2_password认证方式,当使用navicat 或者程序连接是连接不上, 好吧,那我们修改配置并重启服务可以解决此问题 找到mysql的配置文 ...
- Final阶段基于spec评论作品
组名:杨老师粉丝群 组长:乔静玉 组员:吴奕瑶 刘佳瑞 公冶令鑫 杨磊 刘欣 张宇 卢帝同 一.测试目标:拉格朗日2018——飞词 1.开始界面,只有一个开始游戏按钮,简约且醒目 ...
- page = new String(request.getQueryString().getBytes("ISO-8859-1"),"GBK");解决前台传后台乱码问题
page = new String(request.getQueryString().getBytes("ISO-8859-1"),"GBK");解决前台传后台 ...