在系列三中咱們介紹了能夠經過配置文件或者參數傳遞來配置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
只要屬性的值發生變化,該bean發送一個PropertyChange事件給全部已註冊的監聽器。該變化能夠發生在調用set方法時,或者程序的用戶作出某種動做時。spa
爲了使感興趣的監聽器可以進行註冊,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);