Spring中property-placeholder的使用与解析详解

2025-05-29 0 51

我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中.

代码分析的时候,涉及的知识点概要:

1.namespacehandler 解析xml配置文件中的自定义命名空间
2.contextnamespacehandler 上下文相关的解析器,这边定义了具体如何解析property-placeholder的解析器
3.beandefinitionparser 解析bean definition的接口
4.beanfactorypostprocessor 加载好bean definition后可以对其进行修改
5.propertysourcesplaceholderconfigurer 处理bean definition 中的占位符

我们先来看看具体的使用吧

property的使用

在xml文件中配置properties文件

?

1

2

3

4

5

6

7

8

9

10

11

12
<?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文件到环境.

?

1

2

3

4

5

6

7

8
@configuration

@propertysource("classpath:foo.properties")

public class propertieswithjavaconfig {

@bean

public static propertysourcesplaceholderconfigurer propertysourcesplaceholderconfigurer() {

return new propertysourcesplaceholderconfigurer();

}

}

properties的注入与使用

1.java中使用@value注解获取

?

1

2
@value( "${jdbc.url}" )

private string jdbcurl;

还可以添加一个默认值

?

1

2
@value( "${jdbc.url:adefaulturl}" )

private string jdbcurl;

1.在spring的xml配置文件中获取

?

1

2

3
<bean id="datasource">

<property name="url" value="${jdbc.url}" />

</bean>

源码解析

properties配置信息的加载

spring在启动时会通过abstractapplicationcontext#refresh启动容器初始化工作,期间会委托loadbeandefinitions解析xml配置文件.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18
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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
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解析器解析

?

1

2

3

4

5

6

7

8

9

10

11

12
@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

?

1

2

3
public interface beandefinitionparser {

beandefinition parse(element element, parsercontext parsercontext);

}

2.abstractbeandefinitionparser

beandefinitionparser接口的默认抽象实现.spring的拿手好戏,这边提供了很多方便使用的api,并使用模板方法设计模式给子类提供自定义实现的钩子

我们来看看parse时具体的处理逻辑把: 调用钩子parseinternal解析

  1. 生成bean id,使用beannamegenerator生成,或者直接读取id属性
  2. 处理name 与别名aliases
  3. 往容器中注册bean
  4. 进行事件触发

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实例化

?

1

2

3
protected void invokebeanfactorypostprocessors(configurablelistablebeanfactory beanfactory) {

postprocessorregistrationdelegate.invokebeanfactorypostprocessors(beanfactory, getbeanfactorypostprocessors());

}

propertysourcesplaceholderconfigurer实例化完成后,就直接进行触发,并加载信息

?

1

2
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定义解析流程

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

原文链接:https://www.cnblogs.com/leftthen/p/5615066.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Spring中property-placeholder的使用与解析详解 https://www.kuaiidc.com/111666.html

相关文章

发表评论
暂无评论