在系列三中咱们介绍了能够经过配置文件或者参数传递来配置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);