简单从这几个方面描述一下如何使用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时候,将数据写入到文件系统的例子,还不错,如果有这方面的需求可以看看.