[JavaWeb] SpringBoot + EhCache 通过代码形式进行增删缓存
前言:
SpringBoot使用的本地缓存,我查的Baidu大部分都是采用 注解形式来解决了。极少部分是采用 代码形式进行管理缓存。
注解的形式也无法更改 缓存的过期时间。本文采用 Element类进行更改过期时间。
本文支持 缓存持久化。会把缓存更新到磁盘 重启项目缓存也会生效。
引入pom依赖:
1 <!-- cache --> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-cache</artifactId> 5 </dependency> 6 <!-- ehcache缓存 --> 7 <dependency> 8 <groupId>net.sf.ehcache</groupId> 9 <artifactId>ehcache</artifactId> 10 </dependency>
编写ehcache.xml:
1 <ehcache updateCheck="false" dynamicConfig="true"> 2 3 <!--表示当前系统临时目录--> 4 <diskStore path="java.io.tmpdir/ehcache"/> 5 6 <!-- 7 name:缓存名称。 8 maxElementsInMemory:缓存最大个数。 9 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 10 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 11 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 12 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 13 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 14 maxElementsOnDisk:硬盘最大缓存个数。 15 diskPersistent:是否缓存虚拟机重启期数据WhetherthediskstorepersistsbetweenrestartsoftheVirtualMachine.Thedefaultvalueisfalse. 16 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 17 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 18 clearOnFlush:内存数量最大时是否清除。 19 --> 20 21 <defaultCache 22 maxElementsInMemory="1" 23 eternal="false" 24 timeToIdleSeconds="120" 25 timeToLiveSeconds="120" 26 overflowToDisk="true" 27 diskPersistent="true" 28 memoryStoreEvictionPolicy="LRU"/> 29 30 <cache 31 name="xiwiCacheManager" 32 maxElementsInMemory="1" 33 eternal="false" 34 timeToIdleSeconds="120" 35 timeToLiveSeconds="120" 36 overflowToDisk="true" 37 diskPersistent="true" 38 clearOnFlush="false" 39 maxElementsOnDisk="10000000" 40 diskExpiryThreadIntervalSeconds="120" 41 memoryStoreEvictionPolicy="LRU"> 42 <bootstrapCacheLoaderFactory class="net.sf.ehcache.store.DiskStoreBootstrapCacheLoaderFactory" properties="bootstrapAsynchronously=true" /> 43 </cache> 44 45 </ehcache>
编写EhcacheConfig配置类:
1 package com.xiwi.ehcache.common.config; 2 3 import org.springframework.cache.ehcache.EhCacheCacheManager; 4 import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.core.io.ClassPathResource; 8 9 /** 10 * file: EhcacheConfig 11 * date: 2020-10-15 12 * time: 8:52 13 * explain: Ehcache配置类 14 * author: Xiwi 15 */ 16 @Configuration 17 public class EhcacheConfig { 18 19 @Bean 20 public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){ 21 if (bean.getObject() == null) { return null; } 22 return new EhCacheCacheManager(bean.getObject()); 23 } 24 25 @Bean 26 public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){ 27 EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean (); 28 cacheManagerFactoryBean.setConfigLocation (new ClassPathResource("ehcache.xml")); 29 cacheManagerFactoryBean.setShared(true); 30 return cacheManagerFactoryBean; 31 } 32 }
编写EhcacheUtil类:
1 package com.xiwi.ehcache.common.utils; 2 3 import net.sf.ehcache.Cache; 4 import net.sf.ehcache.CacheManager; 5 import net.sf.ehcache.Element; 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 import org.springframework.core.io.ClassPathResource; 9 import org.springframework.stereotype.Component; 10 import org.springframework.util.CollectionUtils; 11 12 import javax.annotation.PostConstruct; 13 import javax.annotation.Resource; 14 import java.io.Serializable; 15 import java.util.ArrayList; 16 import java.util.LinkedHashMap; 17 import java.util.List; 18 import java.util.Map; 19 20 /** 21 * file: EhcacheUtil 22 * date: 2020-10-15 23 * time: 8:51 24 * explain: Ehcache工具类 25 * author: Xiwi 26 */ 27 @Component 28 public class EhcacheUtil { 29 30 private final Logger logger = LoggerFactory.getLogger(EhcacheUtil.class); 31 32 private String cacheManagerName = "xiwiCacheManager"; 33 private Integer MAX_ELEMENTS_IN_MEMORY = 5000; // 内存中最大缓存对象数 34 private boolean OVERFLOW_TO_DISK = true; // 当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中 35 @Resource 36 private CacheManager cacheManager; 37 38 public String getCacheManagerName() { 39 return cacheManagerName; 40 } 41 public void setCacheManagerName(String cacheManagerName) { 42 this.cacheManagerName = cacheManagerName; 43 44 } 45 46 @PostConstruct 47 private void init() { 48 System.setProperty("net.sf.ehcache.enableShutdownHook","true"); 49 } 50 51 /** 52 * 获取缓存 53 * @param key 54 * @return 55 */ 56 public Object get(String key) { 57 try { 58 return getObject(key).getObjectValue(); 59 }catch (Exception e) { 60 e.printStackTrace(); 61 return null; 62 } 63 } 64 65 /** 66 * 获取缓存 67 * @param key 68 * @param clazz 根据某一类型 进行强转 69 * @param <T> 70 * @return 71 */ 72 public <T> T get(String key, Class<T> clazz) { 73 try { 74 return clazz.cast(getObject(key).getObjectValue()); 75 }catch (Exception e) { 76 e.printStackTrace(); 77 return null; 78 } 79 } 80 81 /** 82 * 设置缓存 83 * @param key 84 * @param value 85 * @return 86 */ 87 public boolean set(String key, Object value) { 88 try { 89 getCache().put(new Element(key, value)); 90 shutdown(); 91 return true; 92 }catch (Exception e) { 93 e.printStackTrace(); 94 return false; 95 } 96 } 97 98 /** 99 * 设置缓存 (element级别上设置过期时间) 100 * @param key 101 * @param value 102 * @param time 103 * @return 104 */ 105 public boolean set(String key, Object value, int time) { 106 try { 107 Element element = new Element(key, value); 108 element.setEternal(true); 109 element.setTimeToIdle(time); // 空闲时间 110 element.setTimeToLive(time); // 生存时间 111 getCache().put(element); 112 shutdown(); 113 return true; 114 }catch (Exception e) { 115 e.printStackTrace(); 116 return false; 117 } 118 } 119 120 /** 121 * 删除缓存 122 * @param key 可以传一个值 或多个 123 * @return 124 */ 125 public boolean del(String... key) { 126 try { 127 if (key != null && key.length > 0) { 128 if (key.length == 1) { 129 getCache().remove(key); 130 } else { 131 getCache().remove(CollectionUtils.arrayToList(key)); 132 } 133 } 134 shutdown(); 135 return true; 136 }catch (Exception e) { 137 e.printStackTrace(); 138 return false; 139 } 140 } 141 142 /** 143 * 根据key 获取过期时间 144 * @param key 145 * @return 时间戳 146 */ 147 public Long getExpire(String key) { 148 return getObject(key).getExpirationTime(); 149 } 150 151 /** 152 * 判断key是否存在 153 * @param key 154 * @return true存在 false不存在 155 */ 156 public boolean hasKey(String key) { 157 return getObject(key)!=null ? true : false; 158 } 159 160 /** 161 * 递增 162 * @param key 163 * @param delta 164 * @return 165 */ 166 public boolean incr(String key, Long delta) { 167 try { 168 if (delta < 0) { 169 throw new RuntimeException("递增因子必须大于0"); 170 } 171 172 Element element = getObject(key); 173 long data = element==null ? 0L : getValueToLong(key); 174 data = data + delta; 175 this.cr(key, data, element); 176 shutdown(); 177 return true; 178 }catch (Exception e){ 179 e.printStackTrace(); 180 return false; 181 } 182 } 183 184 /** 185 * 递减 186 * @param key 187 * @param delta 188 * @return 189 */ 190 public boolean decr(String key, Long delta) { 191 try { 192 if (delta < 0) { 193 throw new RuntimeException("递减因子必须大于0"); 194 } 195 Element element = getObject(key); 196 long data = element==null ? 0L : getValueToLong(key); 197 data = data - delta; 198 this.cr(key, data, element); 199 shutdown(); 200 return true; 201 }catch (Exception e){ 202 e.printStackTrace(); 203 return false; 204 } 205 } 206 207 /** 208 * 获取缓存域中的所有键值对 209 * @return 210 */ 211 public List<Map<String, Object>> iterator() { 212 Cache cache = getCache(); 213 List<Map<String, Object>> jsonResult = new ArrayList<>(); 214 if (cache != null) { 215 cache.getAll(cache.getKeys()).forEach((key, value) -> { 216 jsonResult.add(new LinkedHashMap<String, Object>() {{ 217 put("key", value.getObjectKey()); 218 put("value", value.getObjectValue()); 219 put("creation_time", value.getCreationTime()); 220 put("expiration_time", value.getExpirationTime()); 221 put("hit_count", value.getHitCount()); 222 put("last_access_time", value.getLastAccessTime()); 223 put("last_update_time", value.getLastUpdateTime()); 224 put("time_to_idle", value.getTimeToIdle()); 225 put("time_to_live", value.getTimeToLive()); 226 put("version", value.getVersion()); 227 put("is_eternal", value.isEternal()); 228 }}); 229 }); 230 } 231 // shutdown(); 232 return jsonResult; 233 } 234 235 private Cache getCache() { 236 try { 237 /*if (!Arrays.asList(cacheManager.getCacheNames()).contains(cacheManagerName)) { 238 System.out.println("Cache: " + cacheManagerName + "为空,正在建立一个缓存实例..."); 239 *//*Cache cache = new Cache( 240 cacheManagerName, 241 OVERFLOW_TO_DISK ? 1 : MAX_ELEMENTS_IN_MEMORY, // 如果为真: 只要有一个缓存元素,就直接存到硬盘上去 242 OVERFLOW_TO_DISK, 243 false, 244 7200, 245 3600);*//* 246 247 // DiskStoreConfiguration diskStoreConfiguration = new DiskStoreConfiguration(); 248 // diskStoreConfiguration.setPath("java.io.tmpdir/ehcache/test"); 249 // cacheManager.getConfiguration().addDiskStore(diskStoreConfiguration); 250 Cache cache = cacheManager.getCache(cacheManagerName); 251 CacheConfiguration cacheConfiguration = cache.getCacheConfiguration(); 252 // cacheConfiguration.setName(cacheManagerName); 253 // cacheConfiguration.setMaxEntriesLocalHeap(OVERFLOW_TO_DISK ? 1 : MAX_ELEMENTS_IN_MEMORY); 254 // cacheConfiguration.setOverflowToDisk(OVERFLOW_TO_DISK); 255 cacheConfiguration.setDiskPersistent(true); 256 cacheConfiguration.setEternal(false); 257 cacheConfiguration.setTimeToLiveSeconds(7200); 258 cacheConfiguration.setTimeToIdleSeconds(3600); 259 cacheConfiguration.setMemoryStoreEvictionPolicy("LRU"); 260 // cacheConfiguration.persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.LOCALRESTARTABLE)).maxEntriesLocalDisk(0); 261 262 cacheManager.addCache(cache); 263 }*/ 264 265 if(cacheManager == null || cacheManager.getStatus().intValue() != 1){ 266 cacheManager = new CacheManager(new ClassPathResource("ehcache.xml").getInputStream()); 267 } 268 269 return cacheManager.getCache(cacheManagerName); 270 }catch (Exception e) { 271 e.printStackTrace(); 272 return null; 273 } 274 } 275 private Element getObject(String key) { 276 try { 277 return getCache().get(key); 278 }catch (Exception e) { 279 // e.printStackTrace(); 280 return null; 281 } 282 } 283 private boolean cr(String key, Long data, Element element) { 284 try { 285 Element newElement = new Element(key, data); 286 if (element != null) { 287 newElement.setEternal(element.isEternal()); 288 newElement.setTimeToLive(element.getTimeToLive()); 289 newElement.setTimeToIdle(element.getTimeToLive()); 290 newElement.setVersion(element.getVersion()); 291 } 292 getCache().put(newElement); 293 shutdown(); 294 return true; 295 }catch (Exception e){ 296 // e.printStackTrace(); 297 return false; 298 } 299 } 300 private Long getValueToLong(String key) { 301 try { 302 return Long.valueOf(String.valueOf(getObject(key).getObjectValue())); 303 }catch (Exception e) { 304 // e.printStackTrace(); 305 return 0L; 306 } 307 } 308 private void shutdown() { 309 if (OVERFLOW_TO_DISK) { 310 getCache().flush(); 311 // cacheManager.shutdown(); 312 } 313 } 314 }
编写测试 Controller类:
1 package com.xiwi.ehcache.controller; 2 3 import com.xiwi.ehcache.common.utils.EhcacheUtil; 4 import com.xiwi.ehcache.common.utils.User; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.web.bind.annotation.GetMapping; 7 import org.springframework.web.bind.annotation.PathVariable; 8 import org.springframework.web.bind.annotation.RestController; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 13 /** 14 * file: IndexController 15 * date: 2020-10-19 16 * time: 14:48 17 * explain: Ehcache测试控制层 18 * author: Xiwi 19 */ 20 @RestController 21 public class IndexController { 22 23 @Autowired 24 EhcacheUtil ehcacheUtil; 25 26 @GetMapping("/setList/{key}/{time}") 27 public String set( 28 @PathVariable("key") String key, 29 @PathVariable("time") Integer time 30 ) { 31 List<User> list = new ArrayList<>(); 32 list.add(new User(1, "name01", 11)); 33 list.add(new User(2, "name02", 12)); 34 list.add(new User(3, "name03", 13)); 35 list.add(new User(4, "name04", 4)); 36 System.out.println("list.size(): " + list.size()); 37 ehcacheUtil.set(key, list, time); 38 return "setList ok"; 39 } 40 41 @GetMapping("/set/{key}/{value}/{time}") 42 public String set( 43 @PathVariable("key") String key, 44 @PathVariable("value") String value, 45 @PathVariable("time") Integer time 46 ) { 47 ehcacheUtil.set(key, value, time); 48 return "ok"; 49 } 50 51 52 @GetMapping("/get/{key}") 53 public Object get( 54 @PathVariable("key") String key 55 ) { 56 return ehcacheUtil.get(key); 57 } 58 59 @GetMapping("/getCache") 60 public Object getCache() { 61 return ehcacheUtil.iterator(); 62 } 63 64 65 @GetMapping("/incr/{key}/{value}") 66 public Object incr( 67 @PathVariable("key") String key, 68 @PathVariable("value") Long value 69 ) { 70 ehcacheUtil.incr(key, value); 71 return ehcacheUtil.get(key); 72 } 73 74 @GetMapping("/decr/{key}/{value}") 75 public Object decr( 76 @PathVariable("key") String key, 77 @PathVariable("value") Long value 78 ) { 79 ehcacheUtil.decr(key, value); 80 return ehcacheUtil.get(key); 81 } 82 83 @GetMapping("/hasKey/{key}") 84 public boolean a( 85 @PathVariable("key") String key 86 ) { 87 return ehcacheUtil.hasKey(key); 88 } 89 }