我們在基於spring開發應用的時候,一般都會將數據庫的配置放置在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字段來進行排序
Spring3.1添加了@PropertySource注解,方便添加property文件到環境.
@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
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>
Spring在啟動時會通過AbstractApplicationContext#refresh啟動容器初始化工作,期間會委托loadBeanDefinitions解析xml配置文件.
protectedfinalvoidrefreshBeanFactory() 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
protectedvoidparseBeanDefinitions(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
publicvoidinit() {
// 這就是我們要找的解析器
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);
}
AbstractSingleBeanDefinitionParser
解析,定義單個BeanDefinition的抽象父類
在parseInternal中,解析出parentName,beanClass,source;並使用BeanDefinitionBuilder進行封裝
AbstractPropertyLoadingBeanDefinitionParser
解析property相關的屬性,如location,properties-ref,file-encoding,order等
PropertyPlaceholderBeanDefinitionParser
這邊處理的事情不多,就是設置ingore-unresolvable和system-properties-mode
接下來,我們再看看這個bean是在什麼時候實例化的,一般類的實例化有2種,一種是單例系統啟動就實例化;一種是非單例(或者單例懶加載)在getBean時實例化.
這邊的觸發卻是通過BeanFcatoryPostProcessor.
BeanFactoryPostProcessor是在bean實例化前,修改bean definition的,比如bean definition中的占位符就是這邊解決的,而我們現在使用的properties也是這邊解決的.
這個是通過PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors實現的.
掃描容器中的BeanFactoryPostProcessor,找到了這邊需要的PropertySourcesPlaceholderConfigurer,並通過容器的getBean實例化
protectedvoidinvokeBeanFactoryPostProcessors(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#parseStringValue
PropertySourcesPlaceholderConfigurer
覆寫postProcessorBeanFactory api定義解析流程
Spring中如何配置Hibernate事務 http://www.linuxidc.com/Linux/2013-12/93681.htm
Struts2整合Spring方法及原理 http://www.linuxidc.com/Linux/2013-12/93692.htm
基於 Spring 設計並實現 RESTful Web Services http://www.linuxidc.com/Linux/2013-10/91974.htm
Spring-3.2.4 + Quartz-2.2.0集成實例 http://www.linuxidc.com/Linux/2013-10/91524.htm
使用 Spring 進行單元測試 http://www.linuxidc.com/Linux/2013-09/89913.htm
運用Spring注解實現Netty服務器端UDP應用程序 http://www.linuxidc.com/Linux/2013-09/89780.htm
Spring 3.x 企業應用開發實戰 PDF完整高清掃描版+源代碼 http://www.linuxidc.com/Linux/2013-10/91357.htm
Spring 的詳細介紹:請點這裡
Spring 的下載地址:請點這裡