Spring中property-placeholder的使用與解析詳解
我們在基于spring開發(fā)應(yīng)用的時(shí)候,一般都會(huì)將數(shù)據(jù)庫的配置放置在properties文件中.
代碼分析的時(shí)候,涉及的知識點(diǎn)概要:
1.NamespaceHandler 解析xml配置文件中的自定義命名空間
2.ContextNamespaceHandler 上下文相關(guān)的解析器,這邊定義了具體如何解析property-placeholder的解析器
3.BeanDefinitionParser 解析bean definition的接口
4.BeanFactoryPostProcessor 加載好bean definition后可以對其進(jìn)行修改
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文件就會(huì)被spring加載
如果想使用多個(gè)配置文件,可以添加order字段來進(jìn)行排序
使用PropertySource注解配置
Spring3.1添加了@PropertySource注解,方便添加property文件到環(huán)境.
@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;
還可以添加一個(gè)默認(rèn)值
@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
1.在Spring的xml配置文件中獲取
<bean id="dataSource">
<property name="url" value="${jdbc.url}" />
</bean>
源碼解析
properties配置信息的加載
Spring在啟動(dòng)時(shí)會(huì)通過AbstractApplicationContext#refresh啟動(dòng)容器初始化工作,期間會(huì)委托l(wèi)oadBeanDefinitions解析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);
}
}
這邊由于不是標(biāo)準(zhǔn)類定義,所以委托BeanDefinitionParserDelegate解析
通過NamespaceHandler查找到對應(yīng)的處理器是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是這一輪代碼分析的重點(diǎn).
我們來看看它的父類吧.
1.BeanDefinitionParser
被DefaultBeanDefinitionDocumentReader用于解析個(gè)性化的標(biāo)簽
這邊只定義了一個(gè)解析Element的parse api
public interface BeanDefinitionParser {
BeanDefinition parse(Element element, ParserContext parserContext);
}
2.AbstractBeanDefinitionParser
BeanDefinitionParser接口的默認(rèn)抽象實(shí)現(xiàn).spring的拿手好戲,這邊提供了很多方便使用的api,并使用模板方法設(shè)計(jì)模式給子類提供自定義實(shí)現(xiàn)的鉤子
我們來看看parse時(shí)具體的處理邏輯把: 調(diào)用鉤子parseInternal解析
- 生成bean id,使用BeanNameGenerator生成,或者直接讀取id屬性
- 處理name 與別名aliases
- 往容器中注冊bean
- 進(jìn)行事件觸發(fā)
3.AbstractSingleBeanDefinitionParser
解析,定義單個(gè)BeanDefinition的抽象父類
在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder進(jìn)行封裝
4.AbstractPropertyLoadingBeanDefinitionParser
解析property相關(guān)的屬性,如location,properties-ref,file-encoding,order等
5.PropertyPlaceholderBeanDefinitionParser
這邊處理的事情不多,就是設(shè)置ingore-unresolvable和system-properties-mode
properties文件的加載,bean的實(shí)例化
接下來,我們再看看這個(gè)bean是在什么時(shí)候?qū)嵗?一般類的實(shí)例化有2種,一種是單例系統(tǒng)啟動(dòng)就實(shí)例化;一種是非單例(或者單例懶加載)在getBean時(shí)實(shí)例化.
這邊的觸發(fā)卻是通過BeanFcatoryPostProcessor.
BeanFactoryPostProcessor是在bean實(shí)例化前,修改bean definition的,比如bean definition中的占位符就是這邊解決的,而我們現(xiàn)在使用的properties也是這邊解決的.
這個(gè)是通過PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors實(shí)現(xiàn)的.
掃描容器中的BeanFactoryPostProcessor,找到了這邊需要的PropertySourcesPlaceholderConfigurer,并通過容器的getBean實(shí)例化
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
PropertySourcesPlaceholderConfigurer實(shí)例化完成后,就直接進(jìn)行觸發(fā),并加載信息
OrderComparator.sort(priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
我們再來看看PropertySourcesPlaceholderConfigurer的繼承體系把
1.BeanFactoryPostProcessor
定義一個(gè)用于修改容器中bean definition的屬性的接口.其實(shí)現(xiàn)類在一般類使用前先實(shí)例化,并對其他類的屬性進(jìn)行修改.
這跟BeanPostProcessor有明顯的區(qū)別,BeanPostProcessor是修改bean實(shí)例的.
2.PropertiesLoaderSupport
加載properties文件的抽象類.
這邊具體的加載邏輯是委托PropertiesLoaderUtils#fillProperties實(shí)現(xiàn)
3.PropertyResourceConfigurer
bean definition中占位符的替換就是這個(gè)抽象類實(shí)現(xiàn)的.
實(shí)現(xiàn)BeanFactoryPostProcessor#postProcessBeanFactory,迭代容器的中的類定義,進(jìn)行修改
具體如何修改就通過鉤子processProperties交由子類實(shí)現(xiàn)
4.PlaceholderConfigurerSupport
使用visitor設(shè)計(jì)模式,通過BeanDefinitionVisitor和StringValueResolver更新屬性
StringValueResolver是一個(gè)轉(zhuǎn)化String類型數(shù)據(jù)的接口,真正更新屬性的api實(shí)現(xiàn)竟然是在
PropertyPlaceholderHelper#parseStringValue
5.PropertySourcesPlaceholderConfigurer
覆寫postProcessorBeanFactory api定義解析流程
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Spring如何使用PropertyPlaceholderConfigurer讀取文件
- Spring Boot中@ConditionalOnProperty的使用方法
- Spring @value和@PropertySource注解使用方法解析
- Spring Boot自定義配置屬性源(PropertySource)
- Spring boot中PropertySource注解的使用方法詳解
- spring boot 注入 property的三種方式(推薦)
- 詳解Spring Boot 自定義PropertySourceLoader
- spring-core組件詳解——PropertyResolver屬性解決器
- Spring框架讀取property屬性文件常用5種方法
相關(guān)文章
基于Springboot的高校社團(tuán)管理系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
本文將基于Springboot+Mybatis開發(fā)實(shí)現(xiàn)一個(gè)高校社團(tuán)管理系統(tǒng),系統(tǒng)包含三個(gè)角色:管理員、團(tuán)長、會(huì)員。文中采用的技術(shù)有Springboot、Mybatis、Jquery、AjAX、JSP等,感興趣的可以了解一下2022-07-07
Spring Boot Web應(yīng)用開發(fā) CORS 跨域請求支持
本篇文章主要介紹了Spring Boot Web應(yīng)用開發(fā) CORS 跨域請求支持,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
java字符串遍歷以及統(tǒng)計(jì)字符串中各類字符
這篇文章主要為大家詳細(xì)介紹了java字符串遍歷以及字符串中各類字符統(tǒng)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03

