差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

Both sides previous revision 前次修改
下次修改
前次修改
java:guava:suppliers:file_changed_cache [2016/08/12 15:06]
tony
java:guava:suppliers:file_changed_cache [2023/06/25 09:48] (目前版本)
行 1: 行 1:
 {{tag>​java guava}} {{tag>​java guava}}
 ====== File Changed Cache ====== ====== File Changed Cache ======
 +===== Problem =====
 +讀取某個檔案建立model,可能因為成本昂貴而建立Cache;此外,程式可能會因為檔案有修改而重新建立Cache。本篇教學,將告訴你如何透過Guava達到這個需求。
 +===== How to? =====
 +Guava本身有提供ExpiringMemoizingSupplier讓你以時間為條件,去重讀cache;ExpiredFileMemorizeSupplier使用Proxy的概念,去控管何時該真的去讀實際資料。我參考這個做法,建立了ExpiredFileMemorizeSupplier物件,會根據檔案是否修改,去決定是否讀真實資料:​
 <code java> <code java>
 package org.tonylin.practice.guava.cache;​ package org.tonylin.practice.guava.cache;​
行 38: 行 42:
 } }
 </​code>​ </​code>​
 +通常提供資料的物件,本身應為對應檔案的Information Export;因此我另外定義IFileDataSupplier介面去做這事情:​
 <code java> <code java>
 package org.tonylin.practice.guava.cache;​ package org.tonylin.practice.guava.cache;​
行 49: 行 54:
 } }
 </​code>​ </​code>​
 +以下為我的測試案例,去驗證資料是從cache來還是實際讀:​ (這延續之前的測試修改的,並非量身打造,莫見怪)
 +<code java>
 +package org.tonylin.practice.guava.cache;​
  
 +import java.io.File;​
 +import java.io.IOException;​
 +import java.net.MalformedURLException;​
 +import java.nio.charset.Charset;​
 +import java.util.concurrent.TimeUnit;​
 +
 +import org.junit.After;​
 +import org.junit.Assert;​
 +import org.junit.Before;​
 +import org.junit.Test;​
 +
 +import com.google.common.base.Supplier;​
 +import com.google.common.base.Suppliers;​
 +import com.google.common.io.Files;​
 +import com.google.common.io.Resources;​
 +
 +
 +public class TestSuppliers {
 +
 + private int count = 0;
 +
 + private File testFile = new File("​TestSuppliersTestFile.txt"​);​
 +
 + @Before
 + public void setup() throws Exception {
 + Files.write("​test"​.getBytes(),​ testFile);
 + }
 +
 + @After
 + public void teardown(){
 + testFile.delete();​
 + }
 +
 + @Test
 + public void testExpiredFileMemorizeSupplier_IFileDataSupplier() throws Exception {
 + IFileDataSupplier<​String>​ fileDataSupplier = new IFileDataSupplier<​String>​() {
 + private File testFile = new File("​TestSuppliersTestFile.txt"​);​
 +
 + @Override
 + public String get() {
 + try {
 + count++;​
 + return Resources.toString(testFile.toURI().toURL(),​ Charset.forName("​utf-8"​));​
 + } catch (Exception e) {
 + throw new RuntimeException(e);​
 + }
 + }
 +
 + @Override
 + public File getFile() {
 + return testFile;
 + }
 + };
 +
 + ExpiredFileMemorizeSupplier<​String>​ s = new ExpiredFileMemorizeSupplier<>​(fileDataSupplier);​
 +
 + Assert.assertEquals( 0, count);
 + Assert.assertEquals("​test",​ s.get());
 + Assert.assertEquals( 1, count);
 + Assert.assertEquals("​test",​ s.get());
 + Assert.assertEquals( 1, count);
 +
 + Files.write("​test2"​.getBytes(),​ testFile);
 + Assert.assertEquals("​test2",​ s.get());
 + Assert.assertEquals( 2, count);
 + }
 +
 + @Test
 + public void testExpiredFileMemorizeSupplier() throws Exception{
 + ExpiredFileMemorizeSupplier<​String>​ s = new ExpiredFileMemorizeSupplier<>​(this::​getMemoize,​ testFile);
 +
 + Assert.assertEquals( 0, count);
 + Assert.assertEquals("​testMemoize",​ s.get());
 + Assert.assertEquals( 1, count);
 + Assert.assertEquals("​testMemoize",​ s.get());
 + Assert.assertEquals( 1, count);
 +
 + Files.write("​test"​.getBytes(),​ testFile);
 + Assert.assertEquals("​testMemoize",​ s.get());
 + Assert.assertEquals( 2, count);
 + }
 +
 + public String getMemoize(){
 + count++;
 + return "​testMemoize";​
 + }
 +}
 +</​code>​
 +也許應該設計成Event-based的Eviction?​
 +===== 後記 =====
 +在Linux上發現,如果修改時間間格非常短(1秒以下)有可能偵測不出來。如果不在意或不影響的就維持目前作法;如果在意可能就要改用[[https://​docs.oracle.com/​javase/​tutorial/​essential/​io/​notification.html|File WatchService]]。
 +===== Reference =====
 +  * [[https://​github.com/​google/​guava/​wiki/​CachesExplained|Guava - CachesExplained]]
 +=====  =====
 +----
 +\\
 +~~DISQUS~~