差異處
這裏顯示兩個版本的差異處。
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~~ | ||