转:

Spring中property-placeholder的使用与解析

Spring中property-placeholder的使用与解析

我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中.
代码分析的时候,涉及的知识点概要:

  1. NamespaceHandler 解析xml配置文件中的自定义命名空间
  2. ContextNamespaceHandler 上下文相关的解析器,这边定义了具体如何解析property-placeholder的解析器
  3. BeanDefinitionParser 解析bean definition的接口
  4. BeanFactoryPostProcessor 加载好bean definition后可以对其进行修改
  5. 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的注入与使用

  1. java中使用@Value注解获取
@Value( "${jdbc.url}" )
private String jdbcUrl;

还可以添加一个默认值

@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
  1. 在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是这一轮代码分析的重点.
我们来看看它的父类吧.

  1. BeanDefinitionParser
    被DefaultBeanDefinitionDocumentReader用于解析个性化的标签
    这边只定义了一个解析Element的parse api
public interface BeanDefinitionParser {
BeanDefinition parse(Element element, ParserContext parserContext);
}
  1. AbstractBeanDefinitionParser
    BeanDefinitionParser接口的默认抽象实现.spring的拿手好戏,这边提供了很多方便使用的api,并使用模板方法设计模式给子类提供自定义实现的钩子
    我们来看看parse时具体的处理逻辑把:

    • 调用钩子parseInternal解析
    • 生成bean id,使用BeanNameGenerator生成,或者直接读取id属性
    • 处理name 与别名aliases
    • 往容器中注册bean
    • 进行事件触发
  2. AbstractSingleBeanDefinitionParser
    解析,定义单个BeanDefinition的抽象父类
    在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder进行封装

  3. AbstractPropertyLoadingBeanDefinitionParser
    解析property相关的属性,如location,properties-ref,file-encoding,order等

  4. 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的继承体系把

  1. BeanFactoryPostProcessor
    定义一个用于修改容器中bean definition的属性的接口.其实现类在一般类使用前先实例化,并对其他类的属性进行修改.
    这跟BeanPostProcessor有明显的区别,BeanPostProcessor是修改bean实例的.

  2. PropertiesLoaderSupport
    加载properties文件的抽象类.
    这边具体的加载逻辑是委托PropertiesLoaderUtils#fillProperties实现

  3. PropertyResourceConfigurer
    bean definition中占位符的替换就是这个抽象类实现的.
    实现BeanFactoryPostProcessor#postProcessBeanFactory,迭代容器的中的类定义,进行修改
    具体如何修改就通过钩子processProperties交由子类实现

  4. PlaceholderConfigurerSupport
    使用visitor设计模式,通过BeanDefinitionVisitor和StringValueResolver更新属性
    StringValueResolver是一个转化String类型数据的接口,真正更新属性的api实现竟然是在PropertyPlaceholderHelper#parseStringValue

  5. PropertySourcesPlaceholderConfigurer
    覆写postProcessorBeanFactory api定义解析流程

分类: spring

Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载的更多相关文章

  1. spring中 context:property-placeholder 导入多个独立的 .properties配置文件

    spring中 context:property-placeholder 导入多个独立的 .properties配置文件? Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个 o ...

  2. spring中context:property-placeholder/元素 转载

    spring中context:property-placeholder/元素  转载 1.有些参数在某些阶段中是常量 比如 :a.在开发阶段我们连接数据库时的连接url,username,passwo ...

  3. Spring中<context:annotation-config/>

    最近在研究Spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Sp ...

  4. Spring中<context:annotation-config/>的作用

    spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Spring容 ...

  5. 第5章—构建Spring Web应用程序—关于spring中的validate注解后台校验的解析

    关于spring中的validate注解后台校验的解析 在后台开发过程中,对参数的校验成为开发环境不可缺少的一个环节.比如参数不能为null,email那么必须符合email的格式,如果手动进行if判 ...

  6. 【Spring MVC】Properties文件的加载

    [Spring MVC]Properties文件的加载 转载:https://www.cnblogs.com/yangchongxing/p/10726885.html 参考:https://java ...

  7. web.xml中如何设置配置文件的加载路径

    web应用程序通过Tomcat等容器启动时,会首先加载web.xml文件,通常我们工程中的各种配置文件,如日志.数据库.spring的文件等都在此时被加载,下面是两种常用的配置文件加载路径,即配置文件 ...

  8. 用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机

    原文:用MVVM模式开发中遇到的零散问题总结(5)--将动态加载的可视元素保存为图片的控件,Binding刷新的时机 在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打 ...

  9. [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

    [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型 作者:u0u0 - iTyran 在上一节中,我们分析了OBJ格式.OBJ格式优点是文本形式,可读 ...

随机推荐

  1. Luogu P2341 [HAOI2006]受欢迎的牛

    这道题应该也是经典的SCC题了吧 印象中不知道在在班里上课的时候在紫书,ACM竞赛的那些书上看到多少次(有点奇怪) 首先思路很明显,就是要找出有多少个点,以它们为起点可以遍历整个图 首先考虑一种情况, ...

  2. 【个人】爬虫实践,利用xpath方式爬取数据之爬取虾米音乐排行榜

    实验网站:虾米音乐排行榜 网站地址:http://www.xiami.com/chart  难度系数:★☆☆☆☆ 依赖库:request.lxml的etree (安装lxml:pip install ...

  3. 洛咕 P2336 [SCOI2012]喵星球上的点名

    洛咕 P2336 [SCOI2012]喵星球上的点名 先求出SA和height,一个点名串对应的就是一段区间,还有很多个点,就转化成了 有很多个区间,很多个点集,对每个区间计算和多少个点集有交,对每个 ...

  4. ElasticSearch入门 第四篇:使用C#添加和更新文档

    这是ElasticSearch 2.4 版本系列的第四篇: ElasticSearch入门 第一篇:Windows下安装ElasticSearch ElasticSearch入门 第二篇:集群配置 E ...

  5. ElasticSearch入门 第三篇:索引

    这是ElasticSearch 2.4 版本系列的第三篇: ElasticSearch入门 第一篇:Windows下安装ElasticSearch ElasticSearch入门 第二篇:集群配置 E ...

  6. Seay源代码审计系统的配置和安装

    2014年7月31日 Seay源代码审计系统2.1 时隔刚好一年之久,源代码审计系统再次更新,这次主要优化审计体验,优化了漏洞规则,算是小幅更新,原来使用者打开程序会提示自动更新. 1.优化原有规则, ...

  7. 软件测试_测试工具_LoadRunner

    最近正在逐步学习软件测试工具的使用,此文章也是用来当做笔记以供记录之用.如有问题,还请多多指出. 安装LoadRunner基本步骤从网上搜索即可找到,特此提供部分链接参考(其中附带软件下载): 1.L ...

  8. 初学Direct X(7) ——位图的旋转,缩放以及平移

    初学Direct X(7) --位图的旋转,缩放以及平移 本文旨在实现通过D3DXMatrixTransformation2D函数实现位图的旋转,缩放以及平移操作,但是具体的原理部分会在后面进一步的探 ...

  9. java后台面试知识点总结

    本文主要记录在准备面试过程中遇到的一些基本知识点(持续更新) 一.Java基础知识 1.抽象类和接口的区别 接口和抽象类中都可以定义变量,但是接口中定义的必须是公共的.静态的.Final的,抽象类中的 ...

  10. ace -- api

    Ace The main class required to set up an Ace instance in the browser. 在浏览器中设置一个ace实例所需要的主要类. Methods ...