ehcache3-jcache
原生的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统一管理。