1. 程式人生 > >Spring原始碼分析(4)---BeanFactoryPostProcessor(看見的不一定是真的)

Spring原始碼分析(4)---BeanFactoryPostProcessor(看見的不一定是真的)

在第二編對BeanFactory的分析中,我們老能看見BeanFactoyPostProcessor的身影,那麼在這一節中,我們來詳細的討論一下
BeanFactoryPostProcessor的程式碼結構,從中學習他的優秀之處;

BeanFactoryPostProcessor能夠修改bean配置檔案中的bean的定義;使得我們能夠進行一些額外的處理;
在spring中,我們幾乎不用自己擴充套件這個介面,因為他內建了很多實現,但是,我們從原理上和程式碼上來分析其功能的實現;

一下是他的類圖實現:





拿PropertyPlaceholderConfigurer舉例;


PropertyPlaceholderConfigurer的功能是這樣的,我們在配置檔案中配置如下Bean:

  1. <beanid="PropertyPlaceholderConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  2. <propertyname="order">1</property>
  3. <propertyname="locations">
  4. <list>
  5. <value>userinfo.properties</value>
  6. </list>
  7. </property>
  8. </bean>
  9. <beanid="user"class="org.test.UserInfo"
    >
  10. <propertyname="order" value="${db.userName}"></property>
  11. <propertyname="order" value="${db.password}"></property>
  12. </property>
  13. </bean>


userinfo.properties:
db.username:scott
db.password:tiger

然後在ApplicationContext下面會自動呼叫這個PostProcessor;把${db.userName}轉換為scott;
在BeanFactory下面的話,必須手動生成以上PostProcesor物件,並且手動呼叫postProcessorBeanFactory(configureableBeanFactory)方法;

那麼我們現在來看一下程式碼的實現
在PropertyResourceConfigurer中,定義了
postProcessBeanFactory方法,定義了方法執行的流程,使用了模板模式;
將具體演算法的實現暴露到了子類;


public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            Properties mergedProps = mergeProperties();

            // Convert the merged properties, if necessary.
            convertProperties(mergedProps);

            // Let the subclass process the properties.
            processProperties(beanFactory, mergedProps);
        }
        catch (IOException ex) {
            throw new BeanInitializationException("Could not load properties", ex);
        }
    }


模板方法1:mergeProperties()如下:

protected Properties mergeProperties() throws IOException {
        Properties result = new Properties();

        if (this.localOverride) {
            // Load properties from file upfront, to let local properties override.
            loadProperties(result);
        }

        if (this.localProperties != null) {
            for (int i = 0; i < this.localProperties.length; i++) {
                Properties props = this.localProperties[i];
                // Use propertyNames enumeration to also catch default properties.
                for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
                    String key = (String) en.nextElement();
                    result.setProperty(key, props.getProperty(key));
                }
            }
        }

        if (!this.localOverride) {
            // Load properties from file afterwards, to let those properties override.
            loadProperties(result);
        }

        return result;
    }
在這個方法中,將載入你在構造bean的時候傳入的properties值,然後儲存到這個PostProcessor中,方便覆蓋bean定義的元資料,如${db.username}等等;


模板方法processProperties被推到子類實現了:

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
            throws BeansException {
        //構造一個BeanDefinition訪問器(前一節已經分析過),是其內部類:
        BeanDefinitionVisitor visitor = new PlaceholderResolvingBeanDefinitionVisitor(props);

        //得到所有beanName
        String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
        for (int i = 0; i < beanNames.length; i++) {
            // Check that we're not parsing our own bean definition,
            // to avoid failing on unresolvable placeholders in properties file locations.
            if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
                BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);
                try {
                    //在這段程式碼中會遍歷所有的Bean定義的帶來${}的屬性,包括map ,list,String等等;
                    visitor.visitBeanDefinition(bd);
                }
                catch (BeanDefinitionStoreException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i], ex.getMessage());
                }
            }
        }
    }

內部的BeanDefinition訪問器:

private class PlaceholderResolvingBeanDefinitionVisitor extends BeanDefinitionVisitor {

        private final Properties props;

        public PlaceholderResolvingBeanDefinitionVisitor(Properties props) {
            this.props = props;
        }

        protected String resolveStringValue(String strVal) throws BeansException {
            return parseStringValue(strVal, this.props, new HashSet());
        }
    }
在訪問器中visitBeanDefinition(bd)會遍歷此BeanDefinition的proerty,constructor等等可以設定${}的地方例如propertiy:

protected void visitPropertyValues(MutablePropertyValues pvs) {
        PropertyValue[] pvArray = pvs.getPropertyValues();
        for (int i = 0; i < pvArray.length; i++) {
            PropertyValue pv = pvArray[i];
            //分別解決每個屬性欄位的解析;
            Object newVal = resolveValue(pv.getValue());
            if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
                pvs.addPropertyValue(pv.getName(), newVal);
            }
        }
    }
如何解析,是暴露到子類中去進行的如PropertyPlaceholderConfigurer是對${}進行外部檔案的替換,我們也可以自己實現別的替換方式,如:****替換位"corey",很無聊吧: ->;


resolveStringValue((String) value);:

protected Object resolveValue(Object value) {
        if (value instanceof BeanDefinition) {
            visitBeanDefinition((BeanDefinition) value);
        }
        else if (value instanceof BeanDefinitionHolder) {
            visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
        }
        else if (value instanceof RuntimeBeanReference) {
      RuntimeBeanReference ref = (RuntimeBeanReference) value;
      String newBeanName = resolveStringValue(ref.getBeanName());
            if (!newBeanName.equals(ref.getBeanName())) {
                return new RuntimeBeanReference(newBeanName);
            }
        }
        else if (value instanceof List) {
            visitList((List) value);
        }
        else if (value instanceof Set) {
            visitSet((Set) value);
        }
        else if (value instanceof Map) {
            visitMap((Map) value);
        }
        else if (value instanceof TypedStringValue) {
            TypedStringValue typedStringValue = (TypedStringValue) value;
            String visitdString = resolveStringValue(typedStringValue.getValue());
            typedStringValue.setValue(visitdString);
        }
        else if (value instanceof String) {
            return resolveStringValue((String) value);
        }
        return value;
    }

那${userName}舉例:在PropertyPlaceholderConfigurer中:

protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders)
        throws BeanDefinitionStoreException {

        StringBuffer buf = new StringBuffer(strVal);
       
        //提取出${}中間的字串,
        int startIndex = strVal.indexOf(this.placeholderPrefix);
        while (startIndex != -1) {
            int endIndex = buf.toString().indexOf(
                this.placeholderSuffix, startIndex + this.placeholderPrefix.length());
            if (endIndex != -1) {
                String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                if (!visitedPlaceholders.add(placeholder)) {
                    throw new BeanDefinitionStoreException(
                            "Circular placeholder reference '" + placeholder + "' in property definitions");
                }
                //用System.getEnv和外部的properties檔案替代了${}中間的值
                String propVal = resolvePlaceholder(placeholder, props, this.systemPropertiesMode);
                if (propVal != null) {
                    // Recursive invocation, parsing placeholders contained in the
                    // previously resolved placeholder value.
                //巢狀執行;直至無法解析;
                    propVal = parseStringValue(propVal, props, visitedPlaceholders);
                    buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Resolved placeholder '" + placeholder + "' to value [" + propVal + "]");
                    }
                    startIndex = buf.toString().indexOf(this.placeholderPrefix, startIndex + propVal.length());
                }
                else if (this.ignoreUnresolvablePlaceholders) {
                    // Proceed with unprocessed value.
                    startIndex = buf.toString().indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                }
                else {
                    throw new BeanDefinitionStoreException("Could not resolve placeholder '" + placeholder + "'");
                }
                visitedPlaceholders.remove(placeholder);
            }
            else {
                startIndex = -1;
            }
        }

        return buf.toString();
    }


在這裡,模板模式再一次展現了他的魅力,我想在這裡討論一下:PropertiesLoaderSupport和PropertyResourceConfigurer的關係,我們看見PropertiesLoaderSupport提供了properties檔案的載入,在這裡繼承抽象類PropertiesLoaderSupport其實是達到與複用;
我們繼承一個抽象類的原因有兩個:
1):與其它類功能方法之間的複用(比如這裡的PropertiesLoaderSupport);而不是從分類學上面屬於一類,這樣的抽象類屬於工具類;這裡的功能複用,有兩種手段可以實現,一種是組合,一種是繼承;

2):抽象類中約定類流程,把演算法的具體實現暴露給子類;