Spring中property-placeholder的使用與解析

Spring中property-placeholder的使用與解析

咱們在基於spring開發應用的時候,通常都會將數據庫的配置放置在properties文件中.
代碼分析的時候,涉及的知識點概要:java

  1. NamespaceHandler 解析xml配置文件中的自定義命名空間
  2. ContextNamespaceHandler 上下文相關的解析器,這邊定義了具體如何解析property-placeholder的解析器
  3. BeanDefinitionParser 解析bean definition的接口
  4. BeanFactoryPostProcessor 加載好bean definition後能夠對其進行修改
  5. PropertySourcesPlaceholderConfigurer 處理bean definition 中的佔位符

咱們先來看看具體的使用吧node

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文件就會被spring加載
若是想使用多個配置文件,能夠添加order字段來進行排序spring

使用PropertySource註解配置

Spring3.1添加了@PropertySource註解,方便添加property文件到環境.數據庫

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

還能夠添加一個默認值設計模式

@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
  1. 在Spring的xml配置文件中獲取
<bean id="dataSource">
  <property name="url" value="${jdbc.url}" />
</bean>

源碼解析

properties配置信息的加載

Spring在啓動時會經過AbstractApplicationContext#refresh啓動容器初始化工做,期間會委託loadBeanDefinitions解析xml配置文件.api

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解析具體的beanide

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

@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是這一輪代碼分析的重點.
咱們來看看它的父類吧.ui

  1. BeanDefinitionParser
    被DefaultBeanDefinitionDocumentReader用於解析個性化的標籤
    這邊只定義了一個解析Element的parse api
public interface BeanDefinitionParser {
    BeanDefinition parse(Element element, ParserContext parserContext);
}
  1. AbstractBeanDefinitionParser
    BeanDefinitionParser接口的默認抽象實現.spring的拿手好戲,這邊提供了不少方便使用的api,並使用模板方法設計模式給子類提供自定義實現的鉤子
    咱們來看看parse時具體的處理邏輯把:
    • 調用鉤子parseInternal解析
    • 生成bean id,使用BeanNameGenerator生成,或者直接讀取id屬性
    • 處理name 與別名aliases
    • 往容器中註冊bean
    • 進行事件觸發
  2. AbstractSingleBeanDefinitionParser
    解析,定義單個BeanDefinition的抽象父類
    在parseInternal中,解析出parentName,beanClass,source;並使用BeanDefinitionBuilder進行封裝this

  3. AbstractPropertyLoadingBeanDefinitionParser
    解析property相關的屬性,如location,properties-ref,file-encoding,order等

  4. PropertyPlaceholderBeanDefinitionParser
    這邊處理的事情很少,就是設置ingore-unresolvable和system-properties-mode

properties文件的加載,bean的實例化

接下來,咱們再看看這個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的繼承體系把

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

相關文章
相關標籤/搜索