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);
}
}
这边由于<context:property-placeholder location="classpath:dbconf.properties"/>不是标准类定义,所以委托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中property-placeholder的使用与解析的更多相关文章
- Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载
转: Spring中property-placeholder的使用与解析 Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配 ...
- 6.2 dubbo在spring中自定义xml标签源码解析
在6.1 如何在spring中自定义xml标签中我们看到了在spring中自定义xml标签的方式.dubbo也是这样来实现的. 一 META_INF/dubbo.xsd 比较长,只列出<dubb ...
- spring中少用的注解@primary解析
这次看下spring中少见的注解@primary注解,例子 @Component public class MetalSinger implements Singer{ @Override publi ...
- 证明spring中<property name="">这个双引号的内容只与setter方法有关,与一个类定义的字段和getter方法无关
证明如下: 思路定义两个实体类每个实体类的成员变量(字段)名和setter 和getter的名字都不一样: 原因是:bean的声明周期的原因:有一步是:注入属性. 其中一个类引用了另一个类. 被引用类 ...
- Spring 中参数名称解析 - ParameterNameDiscoverer
Spring 中参数名称解析 - ParameterNameDiscoverer Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.ht ...
- Spring中bean的注入方式
首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入.依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖. Spring ...
- 剖析 SPI 在 Spring 中的应用
vivo 互联网服务器团队 - Ma Jian 一.概述 SPI(Service Provider Interface),是Java内置的一种服务提供发现机制,可以用来提高框架的扩展性,主要用于框架的 ...
- spring中解析xml
解析xml有SAX,Stax,dom等方式,那么spring中是如何解析xml文件的呢? Document doc = this.documentLoader.loadDocument( inputS ...
- Spring中AOP相关的API及源码解析
Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...
随机推荐
- Python 中的垃圾回收机制(转载)
from: https://foofish.net/python-gc.html GC作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中无用的垃圾资源 2. 清除这些垃圾并把内存让出来 ...
- js中常用的内部函数的使用
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- 解决:Invalid character found in method name. HTTP method names must be tokens
阿里云上弄了一个tomcat,经常半夜发送崩溃,查看日志发现这个东西,查阅资料发现是Tomcat的header缓冲区大小不够,只需要在server.xml中增加maxHttpHeaderSize字 ...
- edgeR
1)简介 edgeR作用对象是count文件,rows 代表基因,行代表文库,count代表的是比对到每个基因的reads数目.它主要关注的是差异表达分析,而不是定量基因表达水平. edgeR wor ...
- Ubuntu下Anaconda3的安装
1)去https://www.anaconda.com/download/#download下载Anaconda安装文件(python3.6的版本). 2)进入到Anaconda3-5.0.1-Lin ...
- moco操作
1.启动 单个文件启动:将jar包跟启动的文件放在一个文件夹下 命令:java -jar moco-runner-<version>-standalone.jar http -p 12 ...
- Spring Cloud feign
Spring Cloud feign使用 前言 环境准备 应用模块 应用程序 应用启动 feign特性 综上 1. 前言 我们在前一篇文章中讲了一些我使用过的一些http的框架 服务间通信之Http框 ...
- .net 发送邮件失败
1,是否为企业邮箱,如果是则用最高admin的帐号,降低其安全级别,下面的子帐号自动适用.(Google 阻止了从某个不够安全的应用进行的登录尝试) 2,做一个测试页面,对错误结果进行分析,一步一步查 ...
- 日志记录发布网站之后不成功,对路径“C:\Inetpub\wwwroot\***\***.xls”的访问被拒绝。
主要是web程序的根目录文件夹路径访问权限不够,新增加一个everyone的完全控制读写的权限即可!---------折磨了两天,才发现使劲使错了地方. 另外: 一定谨记!!!!! 所写的路径如果不存 ...
- app电池续航上&&下--Android自动化测试学习历程
章节:自动化基础篇——电池续航自动化测试上&&下 主要讲解内容及笔记: 一.影响手机电量的因素和理论: 下面是一个表格:当今主流手机显示屏技术.机型与功耗对照表: 技术 类型 ...