深刻理解Ehcache系列(四)

      在系列三中咱們介紹了能夠經過配置文件或者參數傳遞來配置Ehcache的系統參數。可是若是咱們想動態的去調整這些參數應該怎麼辦呢?java

      這是徹底可行的,Cache提供了相應的方法。安全


Cache cache = manager.getCache("sampleCache");
CacheConfiguration config = cache.getCacheConfiguration();
config
.setTimeToIdleSeconds(60);
config
.setTimeToLiveSeconds(120);
config
.setmaxEntriesLocalHeap(10000);
config
.setmaxEntriesLocalDisk(1000000);


/**
     * @param propertyName
     * @param oldValue
     * @param newValue
     */
    public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
      PropertyChangeSupport pcs;
      synchronized (this) {
        pcs = propertyChangeSupport;
      }
      if (pcs != null && (oldValue != null || newValue != null)) {
        pcs.firePropertyChange(propertyName, oldValue, newValue);
      }
    }



       先讓咱們來回憶一下PropertyChangeSupport的具體用法。app


PropertyChangeSupport類的官方文檔解釋:
      ide

This is a utility class that can be used by beans that support bound properties.  You can use an instance of this class as a member field of your bean and delegate various work to it.



     關聯屬性,也稱綁定屬性。當綁定屬性值發生變化時,通知全部相關的監聽器。爲了實現屬性綁定,必須實現兩個機制。 this


  1. 只要屬性的值發生變化,該bean發送一個PropertyChange事件給全部已註冊的監聽器。該變化能夠發生在調用set方法時,或者程序的用戶作出某種動做時。spa

  2. 爲了使感興趣的監聽器可以進行註冊,bean必須實現如下兩個方法:線程



void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);

    經過java.bean包下的PropertyChangeSupport類來管理監聽器。要使用這個類,bean必須有一個此類的數據域。 debug



private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);


    /**
     * @param listener
     */
    public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
      if (listener != null && propertyChangeSupport != null) {
        propertyChangeSupport.removePropertyChangeListener(listener);
        propertyChangeSupport.addPropertyChangeListener(listener);
      }
    }


    /**
     * @param listener
     */
    public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
      if (listener != null && propertyChangeSupport != null) {
        propertyChangeSupport.removePropertyChangeListener(listener);
      }
    }



    bean的屬性發生變化時,使用PropertyChangeSupport對象的firePropertyChange方法,它會將一個事件發送給全部已經註冊的監聽器。該方法有三個參數:屬性的名字、舊的值以及新的值。屬性的值必須是對象,若是是簡單數據類型,則必須進行包裝。 code

    /**
     * @param propertyName
     * @param oldValue
     * @param newValue
     */
    public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
      PropertyChangeSupport pcs;
      synchronized (this) {
        pcs = propertyChangeSupport;
      }
      if (pcs != null && (oldValue != null || newValue != null)) {
        pcs.firePropertyChange(propertyName, oldValue, newValue);
      }
    }


全部註冊的監聽器實現PropertyChangeListener接口,該接口中有一個方法。 orm


public class RuntimeCfg implements PropertyChangeListener {
     //部分代碼省略

        /**
         * Handles changes to the Configuration this RuntimeCfg backs
         * @param evt the PropertyChangeEvent
         */
        public void propertyChange(final PropertyChangeEvent evt) {
            try {
                DynamicProperty.valueOf(evt.getPropertyName()).applyChange(evt, this);
            } catch (IllegalArgumentException e) {
                throw new IllegalStateException(evt.getPropertyName() + " can't be changed dynamically");
            }
        }

}

   再來看看DynamicProperty作了什麼。

/**
     * Enum of all properties that can change once the Configuration is being used by a CacheManager
     */
    private static enum DynamicProperty {

        cacheManagerName {
            @Override
            void applyChange(final PropertyChangeEvent evt, final RuntimeCfg config) {
                config.cacheManagerName = (String) evt.getNewValue();
            }
        },
        defaultCacheConfiguration {
            @Override
            void applyChange(final PropertyChangeEvent evt, final RuntimeCfg config) {
                LOG.debug("Default Cache Configuration has changed, previously created caches remain untouched");
            }
        },
        maxBytesLocalHeap {
            @Override
            void applyChange(final PropertyChangeEvent evt, final RuntimeCfg config) {

                ArrayList<ConfigError> errors = new ArrayList<ConfigError>();
                Long newValue = (Long)evt.getNewValue();
                if ((Long) evt.getOldValue() > (Long) evt.getNewValue()) {
                    // Double check for over-allocation again
                    for (Cache cache : getAllActiveCaches(config.cacheManager)) {
                        CacheConfiguration cacheConfiguration = cache.getCacheConfiguration();
                        errors.addAll(cacheConfiguration.validateCachePools(config.getConfiguration()));
                        errors.addAll(cacheConfiguration.verifyPoolAllocationsBeforeAddingTo(config.cacheManager,
                            newValue, config.getConfiguration().getMaxBytesLocalOffHeap(), config.getConfiguration().getMaxBytesLocalDisk(), null));
                    }
                }
                if (!errors.isEmpty()) {
                    throw new InvalidConfigurationException("Can't reduce CacheManager byte tuning by so much", errors);
                }
                // Recalculate % based caches
                long cacheAllocated = 0;
                for (Cache cache : getAllActiveCaches(config.cacheManager)) {
                    cache.getCacheConfiguration().configCachePools(config.getConfiguration());
                    long bytesLocalHeap = cache.getCacheConfiguration().getMaxBytesLocalHeap();
                    cacheAllocated += bytesLocalHeap;
                }
                config.cacheManager.getOnHeapPool().setMaxSize(newValue - cacheAllocated);
            }
        },
        maxBytesLocalDisk {
            @Override
            void applyChange(final PropertyChangeEvent evt, final RuntimeCfg config) {
                if ((Long)evt.getOldValue() > (Long)evt.getNewValue()) {
                    // Double check for over-allocation again
                    for (CacheConfiguration cacheConfiguration : config.getConfiguration().getCacheConfigurations().values()) {
                        cacheConfiguration.isMaxBytesLocalDiskPercentageSet();
                    }
                }
                config.cacheManager.getOnDiskPool().setMaxSize((Long) evt.getNewValue());
                // Recalculate % based caches ?
            }
        };

        abstract void applyChange(PropertyChangeEvent evt, RuntimeCfg config);
    }



    使用這個類管理監聽器的好處是,它是線程安全的。若是使用一個循環體來set Bean的屬性,則這個類能夠保證全部監聽器執行觸發事件的有序。 還有一個好處是,這個類支持 fire 帶索引的屬性改變事件(詳見 java.bean.IndexedPropertyChangeEvent )。此時向註冊的監聽器發送一個 PropertyChangeEvent 的方法爲:

void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue);
相關文章
相關標籤/搜索