咱們在基於spring開發應用的時候,通常都會將數據庫的配置放置在properties文件中. 代碼分析的時候,涉及的知識點概要:java
咱們先來看看具體的使用吧node
<?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字段來進行排序spring
Spring3.1添加了@PropertySource註解,方便添加property文件到環境.數據庫
@Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
@Value( "${jdbc.url}" ) private String jdbcUrl;
還能夠添加一個默認值api
@Value( "${jdbc.url:aDefaultUrl}" ) private String jdbcUrl;
在Spring的xml配置文件中獲取ide
<bean id="dataSource"> <property name="url" value="${jdbc.url}" /> </bean>
Spring在啓動時會經過AbstractApplicationContext#refresh啓動容器初始化工做,期間會委託loadBeanDefinitions解析xml配置文件.post
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解析具體的beanui
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解析器解析this
@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); }
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實例化
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#parseStringValue
PropertySourcesPlaceholderConfigurer 覆寫postProcessorBeanFactory api定義解析流程