我们在基于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);
    }
  2. AbstractBeanDefinitionParser
    BeanDefinitionParser接口的默认抽象实现.spring的拿手好戏,这边提供了很多方便使用的api,并使用模板方法设计模式给子类提供自定义实现的钩子
    我们来看看parse时具体的处理逻辑把:
    • 调用钩子parseInternal解析
    • 生成bean id,使用BeanNameGenerator生成,或者直接读取id属性
    • 处理name 与别名aliases
    • 往容器中注册bean
    • 进行事件触发
  3. AbstractSingleBeanDefinitionParser
    解析,定义单个BeanDefinition的抽象父类
    在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder进行封装

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

  5. 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中property-placeholder的使用与解析的更多相关文章

  1. Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载

    转: Spring中property-placeholder的使用与解析 Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配 ...

  2. 6.2 dubbo在spring中自定义xml标签源码解析

    在6.1 如何在spring中自定义xml标签中我们看到了在spring中自定义xml标签的方式.dubbo也是这样来实现的. 一 META_INF/dubbo.xsd 比较长,只列出<dubb ...

  3. spring中少用的注解@primary解析

    这次看下spring中少见的注解@primary注解,例子 @Component public class MetalSinger implements Singer{ @Override publi ...

  4. 证明spring中<property name="">这个双引号的内容只与setter方法有关,与一个类定义的字段和getter方法无关

    证明如下: 思路定义两个实体类每个实体类的成员变量(字段)名和setter 和getter的名字都不一样: 原因是:bean的声明周期的原因:有一步是:注入属性. 其中一个类引用了另一个类. 被引用类 ...

  5. Spring中bean的注入方式

    首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入.依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖. Spring ...

  6. Spring 中参数名称解析 - ParameterNameDiscoverer

    Spring 中参数名称解析 - ParameterNameDiscoverer Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.ht ...

  7. 剖析 SPI 在 Spring 中的应用

    vivo 互联网服务器团队 - Ma Jian 一.概述 SPI(Service Provider Interface),是Java内置的一种服务提供发现机制,可以用来提高框架的扩展性,主要用于框架的 ...

  8. Spring中PropertyPlaceholderConfigurer的使用

    Spring中PropertyPlaceholderConfigurer的使用     在使用Spring配置获取properties文件时,在网上查到相关的资料,分享哈!! (1)获取一个配置文件 ...

  9. Spring中如何向 Bean注入系统属性或环境变量

    [转自] http://unmi.cc/spring-injection-system-properties-env/ 在 Spring 中为 javabean 注入属性文件中的属性值一般人都知道的, ...

  10. Spring中通过变量和import标签来控制加载哪些bean

    需求:根据设置变量,来加载某个spring的bean的配置文件,这个配置文件中,有某些使用的bean.在一些情况下,不希望这些bean被初始化和加载进context中,也不需要被外面访问到. 在spr ...

随机推荐

  1. python里的函数

    map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回. 假设用户输入的英文名字不规范, ...

  2. SpringBoot(一) 入门篇,简单配置

    编辑工具:Intellij IDEA 一.SpringBoot的应用: 1.创建文件 2.项目结构 3.开始构建springboot项目,项目结构 第一步创建Person类: package com. ...

  3. 字体图标Font Awesome 的使用

    下载地址:http://fontawesome.dashgame.com/ 将下载下来的压缩包解压,然后解压,将下载的整个文件夹复制到你的项目中,在你需要用字体图标的html中引入“font-awes ...

  4. 【Python】数据库练习-1

    三十四 数据库 1.     查看数据库命令 2.     使用某个数据库 3.     查看当前在哪个库 4.     查看当前数据库中有哪些表 5.     查询表中数据 6.     建库:cr ...

  5. zookeeper在Dubbo中扮演了一个什么角色

    作者:guxiangfly链接:https://www.zhihu.com/question/25070185/answer/188238271来源:知乎著作权归作者所有.商业转载请联系作者获得授权, ...

  6. 计时器---JS

    <script type="text/javascript"> function count(){ var a=document.getElementById(&quo ...

  7. 复杂的动态布尔表达式性能评估(1)--Antlr4实现

    前言: 规则引擎中, 往往涉及到多个条件构成了复杂布尔表达式的计算. 对于这类布尔表达式, 一是动态可变的(取决于运营人员的设定), 二是其表达式往往很复杂. 如何快速的计算其表达式的值, 该系列文章 ...

  8. Python之路,第四篇:Python入门与基础4

    Python3 字符串 字符串是一个有序的字符序列 如何表示一个字符串: 在非注释中凡是用引号括起来的部分都是字符串: ‘  单引号     ”  双引号      ‘’‘  三单引号    “”“ ...

  9. 牛客国庆集训派对Day4 I-连通块计数(思维,组合数学)

    链接:https://www.nowcoder.com/acm/contest/204/I 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 1048576K,其他语言20 ...

  10. PDO数据库引擎

    PDO概述1.PDO是一种数据库访问抽象层,你不必使用以前的 mysqli_xx 之类只能访问 mysql数据库.使用PDO可以连接mysql.msssql.oracle等等,而不必重写代码.2.PD ...