修改Ehcache快取中取到的值,快取中的值也被修改了

阿步呦發表於2022-03-06

問題現象

我們從Ehcache中取出快取的物件,之後將物件中的屬性進行了修改使用。等再次從快取中拿到物件後,發現物件的值變成了上一次呼叫修改後的物件了。

原因

Ehcache中快取的是原物件的引用,所以引用的內容被修改後cache內部的值也會被修改。

解決方案

使用Ehcache的copyStrategy

Ehcache提供了copyOnRead="true" copyOnWrite="true"的配置屬性。
作用是在讀取或寫入資料時,不使用原始資料,而是使用拷貝資料。
但是在使用該配置的時候,還要提供copyStrategy class屬性,提供Copy策略。

<cache name="bannerCache" eternal="false" maxElementsInMemory="50"
		overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="300"
		timeToLiveSeconds="300" memoryStoreEvictionPolicy="LFU"  copyOnRead="true" copyOnWrite="true">
		<copyStrategy class="com.xxx.EhcacheCopyStrategy" />
</cache>

copy策略類

public class EhcacheCopyStrategy implements ReadWriteCopyStrategy<Element> {
		@Override  
	    public Element copyForWrite(Element value) {  
	        if(value != null){  
	            Object temp=(Serializable)value.getObjectValue(); 
	            try {
				return new Element(value.getObjectKey(),deepCopy(temp));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
	        }  
	        return value;  
	    }  
	    @Override  
	    public Element copyForRead(Element storedValue) {  
	        if(storedValue != null){  
	            Object temp=(Serializable)storedValue.getObjectValue();  
	            try {
				return new Element(storedValue.getObjectKey(),deepCopy(temp));
			} catch (ClassNotFoundException | IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
	        }  
	        return storedValue;  
	    } 
	    
	    private  Object deepCopy(Object src) throws IOException, ClassNotFoundException {  
	        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();  
	        ObjectOutputStream out = new ObjectOutputStream(byteOut);  
	        out.writeObject(src);  
	      
	        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());  
	        ObjectInputStream in = new ObjectInputStream(byteIn);  
	        return in.readObject();  
	    }
}

使用immutable物件

將我們的實體類設計成immutable的,如果需要修改就建立一個新的物件。

如何構建一個immutable物件

  • 確保fields中的成員都被private final修飾;private保證內部成員不會被外部直接訪問,final保證成員在初始化後不會被assigned。
  • 不提供改變成員的方法,例如setXxx。
  • 使用final修飾自定義的類,確保類中的方法不會被重寫。
  • 如果類中的某個成員是mutable型別的,那麼在初始化該成員或者企圖用get方法從外部對其觀察時,應該使用深度拷貝,確保immutable。

String類

String類是java中典型的immutable資料型別,一個String物件一旦唄new出來後,就不能被修改,否則就會報assigned錯誤。
StringBuilder類的物件是mutable的資料型別,當一個StringBuilder物件被建立出來之後,其內部的值是可以通過某些內部方法進行改變的。

相關文章