[轉]Spring中property-placeholder的使用與解析

來源:互聯網
上載者:User

標籤:url   rac   讀取   ota   它的   封裝   turn   bst   key   

我們在基於spring開發應用的時候,一般都會將資料庫的配置放置在properties檔案中.

程式碼分析的時候,涉及的知識點概要:

  1. NamespaceHandler 解析xml設定檔中的自訂命名空間
  2. ContextNamespaceHandler 上下文相關的解析器,這邊定義了具體如何解析property-placeholder的解析器
  3. BeanDefinitionParser 解析bean definition的介面
  4. BeanFactoryPostProcessor 載入好bean definition後可以對其進行修改
  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檔案就會被spring載入
如果想使用多個設定檔,可以添加order欄位來進行排序

使用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設定檔.

    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);        }    }

這邊由於不是標準類定義,所以委託BeanDefinitionParserDelegate解析
通過NamespaceHandler尋找到對應的處理器是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是這一輪程式碼分析的重點.
我們來看看它的父類吧.

  1. BeanDefinitionParser
    被DefaultBeanDefinitionDocumentReader用於解析個人化的標籤
    這邊只定義了一個解析Element的parse api

    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執行個體化

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

[轉]Spring中property-placeholder的使用與解析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.