天天看點

Spring Bean生命周期-prepareRefresh(二)

前面提到了ApplicationContext的執行個體化過程,執行個體化的時候會判斷refresh标志,一般都是true的,在refresh方法中,第一個執行的方法就是prepareRefresh,今天一起看下這個方法内部都做了什麼。

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            // 準備Context的重新整理
            prepareRefresh();
            ....
        }
    }
           

prepareRefresh方法分析

  1. 首先看下ApplicationContext中的prepareRefresh方法内部。
/**
     * Prepare this context for refreshing, setting its startup date and
     * active flag as well as performing any initialization of property sources.
     * 準備重新整理,設定應用開啟的時間還有active标志,并且執行一些屬性源的初始化工作
     */
    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }

        // Initialize any placeholder property sources in the context environment
        // 初始化placeholder屬性
        initPropertySources();

        // Validate that all properties marked as required are resolvable
        // see ConfigurablePropertyResolver#setRequiredProperties
        // 驗證所有的必須的屬性。
        getEnvironment().validateRequiredProperties();

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        // 允許應用啟動之前的事件,當multicaster一旦可用的時候,可用立刻響應釋出的事件。
        this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }

           
  1. initPropertySources,在應用啟動之前替換一些屬性占位符,這個方法再Spring的對象中一般都沒有實作,應該是用來友善我們後期擴充使用的方法。
  2. validateRequiredProperties,Environment類的方法驗證必須的屬性是否正确。
// AbstractEnvironment 類

//private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
//  private final ConfigurablePropertyResolver propertyResolver =
            new PropertySourcesPropertyResolver(this.propertySources);
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
        this.propertyResolver.validateRequiredProperties();
    }
           

validateRequiredProperties代碼分析

  1. 首先看方法内部。
// this.requiredProperties如果沒有操作預設是空的LinkedList
// private final Set<String> requiredProperties = new LinkedHashSet<String>();
public void validateRequiredProperties() {
        MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
        for (String key : this.requiredProperties) {
            if (this.getProperty(key) == null) {
                ex.addMissingRequiredProperty(key);
            }
        }
        if (!ex.getMissingRequiredProperties().isEmpty()) {
            throw ex;
        }
    }
           
  1. PropertyResolver.getProperty
// PropertySourcesPropertyResolver類

  @Override
    public String getProperty(String key) {
        return getProperty(key, String.class, true);
    }

// 擷取屬性内部真正起作用的方法   
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
      //打日志
        if (logger.isTraceEnabled()) {
            logger.trace(String.format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
        }
        
        // this.propertySources上面提到了,傳進來的是個new MutablePropertySources(this.logger),可變的屬性源
        if (this.propertySources != null) {
            for (PropertySource<?> propertySource : this.propertySources) {
                
                Object value;
                // 從propertySource.getProperty中擷取對應的key,并且進行解析
                if ((value = propertySource.getProperty(key)) != null) {
                    Class<?> valueType = value.getClass();
                    
                    // 解析字元串類型的key
                    if (resolveNestedPlaceholders && value instanceof String) {
                        value = resolveNestedPlaceholders((String) value);
                    }
                    
                    // this.conversionService.canConvert轉換擷取的value
                    if (!this.conversionService.canConvert(valueType, targetValueType)) {
                        throw new IllegalArgumentException(String.format(
                                "Cannot convert value [%s] from source type [%s] to target type [%s]",
                                value, valueType.getSimpleName(), targetValueType.getSimpleName()));
                    }
                    return this.conversionService.convert(value, targetValueType);
                }
            }
        }
        if (debugEnabled) {
            logger.debug(String.format("Could not find key '%s' in any property source. Returning [null]", key));
        }
        return null;
    }   
           

可以看出PropertySourcesPropertyResolver擷取key的過程是

  • 先通過PropertySource擷取value
  • 再通過conversionService轉換value為目标類型,預設是轉換為String的是沒有問題的。如果無法轉換抛出異常

小結

ApplicationContext的準備重新整理prepareRefresh邏輯不多,相對簡單一些。不過其中涉及到的解析property的過程需要了解。

其它類

  • PropertyPlaceholderHelper ,parseStringValue方法

最後

一步一步來. 看似慢的方法也許是能最快實作目标的方法。