原生的ehcache只需關注CacheManager和Cache即可,而jcache多了一個cachingProvider,其是用來發現cachingProvider實現的,比如ehcache的EhcacheCachingProvider,然后再通過特定實現的CachingProvider得到特定實現的CacheManager。總的來說,關鍵就是得到合適cachingProvider,之后就與特定的cache框架耦合了。
ehcache的xml配置
1 <config 2 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 3 xmlns='http://www.ehcache.org/v3' 4 xsi:schemaLocation=" 5 http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd"> 6 7 <cache alias="ready-cache"> 8 <key-type>java.lang.Long</key-type> 9 <value-type>com.pany.domain.Product</value-type> 10 <loader-writer> 11 <class>com.pany.ehcache.integration.ProductCacheLoaderWriter</class> 12 </loader-writer> 13 <heap unit="entries">100</heap> 14 </cache> 15 16 </config>
ehcache的jcache api
1 //如果依賴中有多個jcache實現,這里可以指定ehcache實現Caching.getCachingProvider(“org.ehcache.jsr107.EhcacheCachingProvider”);,如果只有一個jcache實現,則Caching.getCachingProvider()可以找到對應的實現,查找邏輯后面有提到 2 CachingProvider cachingProvider = Caching.getCachingProvider(); 3 //getCacheManager第一個參數為一個uri,對於ehcache來說該uri應該是ehcache的配置文件,ehcache實現在這里就會初始化配置文件中的cache 4 CacheManager manager = cachingProvider.getCacheManager( 5 getClass().getResource("/org/ehcache/docs/ehcache-jsr107-config.xml").toURI(), 6 getClass().getClassLoader()); 7 //從manager獲得已配置的cache 8 Cache<Long, Product> readyCache = manager.getCache("ready-cache", Long.class, Product.class);
下面就來看一下ehcache是如何適配jcache的。
getCachingProvider:
getCachingProviders的主要邏輯:
1 public synchronized Iterable<CachingProvider> getCachingProviders(ClassLoader classLoader) { 2 3 final ClassLoader serviceClassLoader = classLoader == null ? getDefaultClassLoader() : classLoader; 4 LinkedHashMap<String, CachingProvider> providers = cachingProviders.get(serviceClassLoader); 5 6 if (providers == null) { 7 //如果系統參數中設置了javax.cache.spi.CachingProvider相關值,則加載該值對應的類 8 if (System.getProperties().containsKey(JAVAX_CACHE_CACHING_PROVIDER)) { 9 String className = System.getProperty(JAVAX_CACHE_CACHING_PROVIDER); 10 providers = new LinkedHashMap<String, CachingProvider>(); 11 providers.put(className, loadCachingProvider(className, serviceClassLoader)); 12 13 } else { 14 providers = AccessController.doPrivileged(new PrivilegedAction<LinkedHashMap<String, CachingProvider>>() { 15 @Override 16 public LinkedHashMap<String, CachingProvider> run() { 17 LinkedHashMap<String, CachingProvider> result = new LinkedHashMap<String, CachingProvider>(); 18 //ServiceLoader.load會lazy加載所以符合條件的類 19 ServiceLoader<CachingProvider> serviceLoader = ServiceLoader.load(CachingProvider.class, serviceClassLoader); 20 //這里serviceLoader.iterator().next()會真正進行加載, 21 //如果依賴中包含多個jcache實現,最終的result就會有多個元素 22 for (CachingProvider provider : serviceLoader) { 23 result.put(provider.getClass().getName(), provider); 24 } 25 return result; 26 } 27 }); 28 29 } 30 31 cachingProviders.put(serviceClassLoader, providers); 32 } 33 34 return providers.values(); 35 }
ServiceLoader.load的主要邏輯:
注意,Class的getResource的路徑可以以“/”開頭,以“/”開頭代表相對於運行環境的根路徑(通常是com的父目錄),不以“/”開頭代表相對於該Class所在路徑的路徑;而ClassLoader的getResource的路徑不能以“/”開頭,與Class以“/”開頭類似。
ehcache類庫下的META-INF/services/
打開該文件,發現ehcache的cachingProvider實現類的全限定名:
CachingProvider.getCacheManager有這樣一個重載方法,將ehcache配置文件路徑傳入uri即可,這樣ehcache就可以使用jcache的api,並使用自己的配置文件了:
總結一下,Caching.getCachingProvider會去找特定的配置文件META-INF/services/javax.cache.spi.CachingProvider,特定的實現會在該文件內寫入CachingProvider實現類的全限定名,Caching.getCachingProvider得到類名后就可以加載該類,最終返回特定實現的CachingProvider,如EhcacheCachingProvider,再之后就是Ehcache的事了。
為什么要有CachingProvider呢?jcache直接提供CacheManager的api不行嗎?我覺得是可以的,只不過沒有了CachingProvider就沒有了對CacheManager的統一管理,我們來看CachingProvider接口,它提供了get(獲取CacheManager)、close(關閉CacheManager)的方法,這樣我們就可以對CacheManager統一管理。