简单从这几个方面描述一下如何使用Cache,对Cache的各种原理介绍此处不涉及.
1.使用场景
2.如何使用Cache
3.创建方式
4. 如何和Spring搭配使用
+------------------------------------------------------分割线-------------------------------------------------------+
1. Cache的使用场景
一般而言,对于那些频繁需要查询比对的热点数据,我们采用使用缓存,对于数据量较小的,几条,几十条数据,而且需要加缓存的接口较少,这时候我们会采用Cache,建议使用Google提供的guava Cache,它简单易用的同时,性能也好. 而且线程安全(原因看源码) .对于那些较大数据量的,或者需要加缓存的接口较多的项目,可以去考虑Redis,memcached等等
2. 如何使用Cache
和Map的使用方式差不多,也可以和Spring结合,使用@Cacheable注解使用.
3. 创建方式
1. Cache Callable
2. LoadingCache
方式一:
1 package info.sanaulla.cache; 2 3 import com.google.common.cache.Cache; 4 import com.google.common.cache.CacheBuilder; 5 import org.junit.Test; 6 7 import java.util.concurrent.Callable; 8 import java.util.concurrent.ExecutionException; 9 import java.util.concurrent.TimeUnit; 10 11 /** 12 * ********************************************************* 13 * <p/> 14 * Author: XiJun.Gong 15 * Date: 2016-08-17 16:59 16 * Version: default 1.0.0 17 * Class description: 18 * <p/> 19 * ********************************************************* 20 */ 21 public class CacheDemo { 22 private static Cache<Object, Object> cache = CacheBuilder.newBuilder() 23 .maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS) 24 .recordStats() 25 .build(); 26 27 public static Object get(Object key) throws ExecutionException { 28 29 Object var = cache.get(key, new Callable<Object>() { 30 @Override 31 public Object call() throws Exception { 32 System.out.println("如果没有值,就执行其他方式去获取值"); 33 String var = "Google.com.sg"; 34 return var; 35 } 36 }); 37 return var; 38 } 39 40 public static void put(Object key, Object value) { 41 cache.put(key, value); 42 } 43 44 class Person { 45 private String name; 46 private Integer age; 47 48 public Person() { 49 } 50 51 public Person(String name, Integer age) { 52 this.name = name; 53 this.age = age; 54 } 55 56 public String getName() { 57 return name; 58 } 59 60 public void setName(String name) { 61 this.name = name; 62 } 63 64 public Integer getAge() { 65 return age; 66 } 67 68 public void setAge(Integer age) { 69 this.age = age; 70 } 71 72 @Override 73 public String toString() { 74 return "Person{" + 75 "名字='" + name + '\'' + 76 ", 年纪=" + age + 77 '}'; 78 } 79 } 80 81 @Test 82 public void CacheTest() throws ExecutionException { 83 84 Person person = new Person(); 85 person.setAge(11); 86 person.setName("tSun"); 87 System.out.println(CacheDemo.get("man")); 88 CacheDemo.put("man", new Person("hopg", 123)); 89 System.out.println(CacheDemo.get("man")); 90 System.out.println(CacheDemo.get("man")); 91 92 System.out.println(CacheDemo.get("person").toString()); 93 CacheDemo.put("person", person); 94 System.out.println(CacheDemo.get("person").toString()); 95 System.out.println(CacheDemo.get("person").toString()); 96 97 System.out.println(CacheDemo.get("woman")); 98 CacheDemo.put("women", new Person("google", 666)); 99 System.out.println(CacheDemo.get("woman")); 100 System.out.println(CacheDemo.get("woman")); 101 System.out.println(CacheDemo.get("man")); 102 } 103 }
结果:
1 如果没有值,就执行其他方式去获取值 2 Google.com.sg 3 Person{名字='hopg', 年纪=123} 4 Person{名字='hopg', 年纪=123} 5 如果没有值,就执行其他方式去获取值 6 Google.com.sg 7 Person{名字='tSun', 年纪=11} 8 Person{名字='tSun', 年纪=11} 9 如果没有值,就执行其他方式去获取值 10 Google.com.sg 11 Google.com.sg 12 Google.com.sg 13 Person{名字='hopg', 年纪=123}
方式二:
1 package info.sanaulla.cache; 2 3 import com.google.common.cache.CacheBuilder; 4 import com.google.common.cache.CacheLoader; 5 import com.google.common.cache.LoadingCache; 6 import org.junit.Test; 7 8 import java.util.concurrent.ExecutionException; 9 import java.util.concurrent.TimeUnit; 10 11 /** 12 * ********************************************************* 13 * <p/> 14 * Author: XiJun.Gong 15 * Date: 2016-08-17 15:00 16 * Version: default 1.0.0 17 * Class description: 18 * <p>Cache Demo</p> 19 * <p/> 20 * ********************************************************* 21 */ 22 public class CacheUtil { 23 24 25 private static LoadingCache<Object, Object> cache = CacheBuilder.newBuilder() 26 .maximumSize(2) 27 .expireAfterAccess(24, TimeUnit.HOURS) 28 .recordStats() 29 .build(new CacheLoader<Object, Object>() { 30 31 @Override 32 public Object load(Object key) throws Exception { 33 return key; 34 } 35 }); 36 37 public static Object get(Object key) throws ExecutionException { 38 Object var = cache.get(key); 39 40 if (var.equals(key)) { 41 42 System.out.println("执行其他操作,查询该值"); 43 /**执行其他操作,获取值**/ 44 Object object = "Google.com.hk"; 45 put(key, object); 46 } else { 47 System.out.println("从Cache中取值...."); 48 } 49 return cache.get(key); 50 } 51 52 public static void put(Object key, Object value) { 53 cache.put(key, value); 54 } 55 56 class Person { 57 private String name; 58 private Integer age; 59 60 public Person() { 61 } 62 63 public Person(String name, Integer age) { 64 this.name = name; 65 this.age = age; 66 } 67 68 public String getName() { 69 return name; 70 } 71 72 public void setName(String name) { 73 this.name = name; 74 } 75 76 public Integer getAge() { 77 return age; 78 } 79 80 public void setAge(Integer age) { 81 this.age = age; 82 } 83 84 @Override 85 public String toString() { 86 return "Person{" + 87 "名字='" + name + '\'' + 88 ", 年纪=" + age + 89 '}'; 90 } 91 } 92 93 @Test 94 public void TestCache() throws ExecutionException { 95 96 Person person = new Person(); 97 person.setAge(11); 98 person.setName("tSun"); 99 System.out.println(CacheUtil.get("man")); 100 CacheUtil.put("man", new Person("hopg", 123)); 101 System.out.println(CacheUtil.get("man")); 102 System.out.println(CacheUtil.get("man")); 103 104 System.out.println(CacheUtil.get("person").toString()); 105 CacheUtil.put("person", person); 106 System.out.println(CacheUtil.get("person").toString()); 107 System.out.println(CacheUtil.get("person").toString()); 108 109 System.out.println(CacheUtil.get("woman")); 110 CacheUtil.put("women", new Person("google", 666)); 111 System.out.println(CacheUtil.get("woman")); 112 System.out.println(CacheUtil.get("woman")); 113 System.out.println(CacheUtil.get("man")); 114 } 115 }
结果:
1 执行其他操作,查询该值 2 Google.com.hk 3 从Cache中取值.... 4 Person{名字='hopg', 年纪=123} 5 从Cache中取值.... 6 Person{名字='hopg', 年纪=123} 7 执行其他操作,查询该值 8 Google.com.hk 9 从Cache中取值.... 10 Person{名字='tSun', 年纪=11} 11 从Cache中取值.... 12 Person{名字='tSun', 年纪=11} 13 执行其他操作,查询该值 14 Google.com.hk 15 从Cache中取值.... 16 Google.com.hk 17 从Cache中取值.... 18 Google.com.hk 19 执行其他操作,查询该值 20 Google.com.hk
4. 如何和Spring结合使用
因为我们需要使用Spring的注解,所以需要重写Spring的一些接口,然后进行自定义.
4.1 首先简单了解一下@Cacheable,@CachePut,@CacheEvit
对于cache和数据操作进行一个功能对应,如下图.
cache sql
Cacheable --save/insert
关于Cacheable的简单说明:
@Cacheable注解,如果是类被注解,那么该类所有的方法下,如果在查询时,会先去查询缓存,没有的话,再去调用方法查询,
并且方法的返回值都会被缓存,如果是方法被注解,那么查询的时候,也会遵从先缓存,然后在方法,并且该方法的返回值都会被缓存.
CachePut --update/Insert
CacheEvit --remove/delete
4.2 首先我们需要实现接口Spring的BeanAware接口,以及InitializingBean接口,并实现FactoryBean接口,还有实现Spring的
AbstractTransactionSupportingCacheManager抽象类
过程大致如下:
1. 实现Cache接口
1 import com.google.common.cache.CacheBuilder; 2 import com.google.common.cache.CacheBuilderSpec; 3 import org.springframework.cache.Cache; 4 import org.springframework.cache.support.SimpleValueWrapper; 5 6 import java.io.Serializable; 7 import java.util.concurrent.TimeUnit; 8 9 import static com.google.common.base.Preconditions.checkNotNull; 10 11 /** 12 * ********************************************************* 13 * <p/> 14 * Author: XiJun.Gong 15 * Date: 2016-08-22 15:47 16 * Version: default 1.0.0 17 * Class description: 18 * <p/> 19 * ********************************************************* 20 */ 21 public class GuavaCache implements Cache { 22 23 24 private static final Object NULL_HOLDER = new NullHolder(); 25 26 private final String name; 27 28 private final com.google.common.cache.Cache<Object, Object> store; 29 30 private final boolean allowNullValues; 31 32 /** 33 * Create a new GuavaCache with the specified name. 34 * 35 * @param name the name of the cache 36 */ 37 public GuavaCache(String name) { 38 this(name, CacheBuilder.newBuilder(), true); 39 } 40 41 /** 42 * Create a new GuavaCache with the specified name. 43 * 44 * @param name the name of the cache 45 * @param allowNullValues whether to accept and convert null values for this cache 46 */ 47 public GuavaCache(String name, boolean allowNullValues) { 48 this(name, CacheBuilder.newBuilder(), allowNullValues); 49 } 50 51 /** 52 * Create a new GuavaCache using the specified name and {@link com.google.common.cache.CacheBuilderSpec specification} 53 * 54 * @param name the name of the cache 55 * @param spec the cache builder specification to use to build he cache 56 */ 57 public GuavaCache(String name, CacheBuilderSpec spec, boolean allowNullValues) { 58 this(name, CacheBuilder.from(spec), allowNullValues); 59 } 60 61 /** 62 * Create a new GuavaCache using the specified name and {@link CacheBuilderSpec specification} 63 * 64 * @param name the name of the cache 65 * @param builder the cache builder to use to build the cache 66 */ 67 public GuavaCache(String name, CacheBuilder<Object, Object> builder, boolean allowNullValues) { 68 this.name = checkNotNull(name, "name is required"); 69 this.allowNullValues = allowNullValues; 70 this.store = builder.maximumSize(CacheConstant.defaultCacheSize). 71 expireAfterWrite(CacheConstant.defaultCacheExpire, TimeUnit.MINUTES). 72 build(); 73 } 74 75 @Override 76 public String getName() { 77 return this.name; 78 } 79 80 @Override 81 public com.google.common.cache.Cache<Object, Object> getNativeCache() { 82 return this.store; 83 } 84 85 @Override 86 public ValueWrapper get(Object key) { 87 Object value = this.store.getIfPresent(key); 88 return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); 89 } 90 91 @Override 92 public void put(Object key, Object value) { 93 this.store.put(key, value); 94 } 95 96 /** 97 * remove the key of object 98 * 99 * @param key 100 */ 101 @Override 102 public void evict(Object key) { 103 this.store.invalidate(key); 104 } 105 106 /** 107 * clear all 108 */ 109 @Override 110 public void clear() { 111 this.store.invalidateAll(); 112 } 113 114 /** 115 * Convert the given value from the internal store to a user value 116 * returned from the get method (adapting {@code null}). 117 * 118 * @param storeValue the store value 119 * @return the value to return to the user 120 */ 121 protected Object fromStoreValue(Object storeValue) { 122 if (this.allowNullValues && storeValue == NULL_HOLDER) { 123 return null; 124 } 125 return storeValue; 126 } 127 128 /** 129 * Convert the given user value, as passed into the put method, 130 * to a value in the internal store (adapting {@code null}). 131 * 132 * @param userValue the given user value 133 * @return the value to store 134 */ 135 protected Object toStoreValue(Object userValue) { 136 if (this.allowNullValues && userValue == null) { 137 return NULL_HOLDER; 138 } 139 return userValue; 140 } 141 142 143 @SuppressWarnings("serial") 144 private static class NullHolder implements Serializable { 145 146 } 147 }
2.实现Spring的FactoryBean,BeanAware,InitializingBean接口
1 import com.google.common.cache.CacheBuilder; 2 import org.springframework.beans.factory.BeanNameAware; 3 import org.springframework.beans.factory.FactoryBean; 4 import org.springframework.beans.factory.InitializingBean; 5 import org.springframework.util.StringUtils; 6 7 /** 8 * ********************************************************* 9 * <p/> 10 * Author: XiJun.Gong 11 * Date: 2016-08-22 16:00 12 * Version: default 1.0.0 13 * Class description: 14 * <p>{@link FactoryBean} for easy configuration of a {@link GuavaCache}.</p> 15 * <p/> 16 * ********************************************************* 17 */ 18 public class GuavaCacheFactoryBean 19 implements FactoryBean<GuavaCache>, BeanNameAware, InitializingBean { 20 21 private String name = ""; 22 23 private boolean allowNullValues = true; 24 25 private String spec; 26 27 private GuavaCache cache; 28 29 public void setName(String name) { 30 this.name = name; 31 } 32 33 public void setAllowNullValues(boolean allowNullValues) { 34 this.allowNullValues = allowNullValues; 35 } 36 37 public void setSpec(String spec) { 38 this.spec = spec; 39 } 40 41 @Override 42 public void setBeanName(String name) { 43 if (!StringUtils.hasLength(this.name)) { 44 this.name = name; 45 } 46 } 47 48 @Override 49 public void afterPropertiesSet() throws Exception { 50 if (StringUtils.hasText(this.spec)) { 51 this.cache = new GuavaCache(this.name, CacheBuilder.from(spec), allowNullValues); 52 } else { 53 this.cache = new GuavaCache(this.name, allowNullValues); 54 } 55 } 56 57 @Override 58 public GuavaCache getObject() throws Exception { 59 return this.cache; 60 } 61 62 @Override 63 public Class<?> getObjectType() { 64 return GuavaCache.class; 65 } 66 67 @Override 68 public boolean isSingleton() { 69 return true; 70 } 71 72 }
3.实现Spring的Manager类
1 import com.google.common.cache.CacheBuilder; 2 import com.google.common.cache.CacheLoader; 3 import org.springframework.cache.Cache; 4 import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager; 5 import org.springframework.util.StringUtils; 6 7 import java.util.Collection; 8 import java.util.Collections; 9 import java.util.concurrent.TimeUnit; 10 11 /** 12 * ********************************************************* 13 * <p/> 14 * Author: XiJun.Gong 15 * Date: 2016-08-22 16:09 16 * Version: default 1.0.0 17 * Class description: 18 * <p> {@link org.springframework.cache.CacheManager} implementation backed by {@link GuavaCache}.</p> 19 * <p/> 20 * ********************************************************* 21 */ 22 public class GuavaCacheManager extends AbstractTransactionSupportingCacheManager { 23 private Collection<GuavaCache> caches; 24 25 private String spec; 26 27 private volatile CacheBuilder<Object, Object> cacheBuilder; 28 29 private boolean allowNullValues = true; 30 31 public GuavaCacheManager() { 32 } 33 34 public void setCaches(Collection<GuavaCache> caches) { 35 this.caches = caches; 36 } 37 38 public void setSpec(String spec) { 39 this.spec = spec; 40 } 41 42 public String getSpec() { 43 return spec; 44 } 45 46 public void setAllowNullValues(boolean allowNullValues) { 47 this.allowNullValues = allowNullValues; 48 } 49 50 public boolean isAllowNullValues() { 51 return allowNullValues; 52 } 53 54 @Override 55 protected Collection<? extends Cache> loadCaches() { 56 return (caches != null) ? caches : Collections.<GuavaCache>emptyList(); 57 } 58 59 @Override 60 public Cache getCache(String name) { 61 Cache cache = super.getCache(name); 62 if (cache == null) { 63 // create a new cache 64 cache = createGuavaCache(name); 65 66 // add to collection of available caches 67 addCache(cache); 68 } 69 return cache; 70 } 71 72 private GuavaCache createGuavaCache(String name) { 73 // create GuavaCache 74 return new GuavaCache(name, getCacheBuilder(), allowNullValues); 75 } 76 77 private CacheBuilder<Object, Object> getCacheBuilder() { 78 if (cacheBuilder == null) { 79 synchronized (this) { 80 if (cacheBuilder == null) { 81 if (StringUtils.hasText(spec)) { 82 cacheBuilder = CacheBuilder.from(spec); 83 } else { 84 cacheBuilder =CacheBuilder.newBuilder(); 85 } 86 87 } 88 notify(); 89 } 90 } 91 92 return cacheBuilder; 93 } 94 95 }
4.3 配置spring配置文件applicationContext.xml
1 <!--添加Cache--> 2 <!--添加一个注解驱动不要掉--> 3 <tx:annotation-driven/> 4 <!--使用spring注解去扫描需要加缓存地方的的包--> 5 <context:component-scan base-package="com.qunar.data.allinone.bus.testModel"> 6 <context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/> 7 </context:component-scan> 8 <!-- cache Manager--> 9 <bean id="cacheManager" class="com.qunar.data.allinone.bus.cache.GuavaCacheManager"> 10 <property name="caches"> 11 <list> 12 <bean class="com.qunar.data.allinone.bus.cache.GuavaCacheFactoryBean" name="msg-cache"/> 13 </list> 14 </property> 15 </bean> 16 <!--cache的注解驱动包--> 17 <cache:annotation-driven/>
测试即可
1 import org.springframework.cache.annotation.Cacheable; 2 import org.springframework.stereotype.Component; 3 import org.springframework.stereotype.Service; 4 5 /** 6 * ********************************************************* 7 * <p/> 8 * Author: XiJun.Gong 9 * Date: 2016-08-22 19:50 10 * Version: default 1.0.0 11 * Class description: 12 * <p/> 13 * ********************************************************* 14 */ 15 @Component 16 public class TestName { 17 18 @Cacheable(value = "msg-cache") 19 public String getName(String con) { 20 System.out.println("缓存中没有找到信息"); 21 return con; 22 } 23 }
1 import com.qunar.data.allinone.bus.testModel.TestName; 2 import org.junit.Test; 3 import org.junit.runner.RunWith; 4 import org.springframework.test.context.ContextConfiguration; 5 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 6 7 import javax.annotation.Resource; 8 9 /** 10 * ********************************************************* 11 * <p/> 12 * Author: XiJun.Gong 13 * Date: 2016-08-22 19:30 14 * Version: default 1.0.0 15 * Class description: 16 * <p/> 17 * ********************************************************* 18 */ 19 @RunWith(SpringJUnit4ClassRunner.class) 20 @ContextConfiguration(value = "classpath:applicationContext.xml") 21 public class CacheTest { 22 23 24 @Resource 25 26 TestName testName; 27 28 @Test 29 public void testName() { 30 String username = "xijun.gong"; 31 for (int i = 0; i < 10; i++) { 32 System.out.println("++++++++++++++++打印结果: " + testName.getName(username)); 33 } 34 } 35 }
缓存中没有找到信息 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二 ++++++++++++++++打印结果: 王小二
5. 扩展
在github上看到一篇关于,对于overflow时候,将数据写入到文件系统的例子,还不错,如果有这方面的需求可以看看.
地址:https://github.com/raphw/guava-cache-overflow-extension