我们在基于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解析
- 生成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实例化
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定义解析流程
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.cnblogs.com/leftthen/p/5615066.html