Guava------------Cache使用方法

时间:2023-03-09 15:32:43
Guava------------Cache使用方法

简单从这几个方面描述一下如何使用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

方式一:

 package info.sanaulla.cache;

 import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.junit.Test; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-17 16:59
* Version: default 1.0.0
* Class description:
* <p/>
* *********************************************************
*/
public class CacheDemo {
private static Cache<Object, Object> cache = CacheBuilder.newBuilder()
.maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS)
.recordStats()
.build(); public static Object get(Object key) throws ExecutionException { Object var = cache.get(key, new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.println("如果没有值,就执行其他方式去获取值");
String var = "Google.com.sg";
return var;
}
});
return var;
} public static void put(Object key, Object value) {
cache.put(key, value);
} class Person {
private String name;
private Integer age; public Person() {
} public Person(String name, Integer age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"名字='" + name + '\'' +
", 年纪=" + age +
'}';
}
} @Test
public void CacheTest() throws ExecutionException { Person person = new Person();
person.setAge(11);
person.setName("tSun");
System.out.println(CacheDemo.get("man"));
CacheDemo.put("man", new Person("hopg", 123));
System.out.println(CacheDemo.get("man"));
System.out.println(CacheDemo.get("man")); System.out.println(CacheDemo.get("person").toString());
CacheDemo.put("person", person);
System.out.println(CacheDemo.get("person").toString());
System.out.println(CacheDemo.get("person").toString()); System.out.println(CacheDemo.get("woman"));
CacheDemo.put("women", new Person("google", 666));
System.out.println(CacheDemo.get("woman"));
System.out.println(CacheDemo.get("woman"));
System.out.println(CacheDemo.get("man"));
}
}

结果:

 如果没有值,就执行其他方式去获取值
Google.com.sg
Person{名字='hopg', 年纪=123}
Person{名字='hopg', 年纪=123}
如果没有值,就执行其他方式去获取值
Google.com.sg
Person{名字='tSun', 年纪=11}
Person{名字='tSun', 年纪=11}
如果没有值,就执行其他方式去获取值
Google.com.sg
Google.com.sg
Google.com.sg
Person{名字='hopg', 年纪=123}

方式二:

 package info.sanaulla.cache;

 import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-17 15:00
* Version: default 1.0.0
* Class description:
* <p>Cache Demo</p>
* <p/>
* *********************************************************
*/
public class CacheUtil { private static LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
.maximumSize(2)
.expireAfterAccess(24, TimeUnit.HOURS)
.recordStats()
.build(new CacheLoader<Object, Object>() { @Override
public Object load(Object key) throws Exception {
return key;
}
}); public static Object get(Object key) throws ExecutionException {
Object var = cache.get(key); if (var.equals(key)) { System.out.println("执行其他操作,查询该值");
/**执行其他操作,获取值**/
Object object = "Google.com.hk";
put(key, object);
} else {
System.out.println("从Cache中取值....");
}
return cache.get(key);
} public static void put(Object key, Object value) {
cache.put(key, value);
} class Person {
private String name;
private Integer age; public Person() {
} public Person(String name, Integer age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"名字='" + name + '\'' +
", 年纪=" + age +
'}';
}
} @Test
public void TestCache() throws ExecutionException { Person person = new Person();
person.setAge(11);
person.setName("tSun");
System.out.println(CacheUtil.get("man"));
CacheUtil.put("man", new Person("hopg", 123));
System.out.println(CacheUtil.get("man"));
System.out.println(CacheUtil.get("man")); System.out.println(CacheUtil.get("person").toString());
CacheUtil.put("person", person);
System.out.println(CacheUtil.get("person").toString());
System.out.println(CacheUtil.get("person").toString()); System.out.println(CacheUtil.get("woman"));
CacheUtil.put("women", new Person("google", 666));
System.out.println(CacheUtil.get("woman"));
System.out.println(CacheUtil.get("woman"));
System.out.println(CacheUtil.get("man"));
}
}

结果:

 执行其他操作,查询该值
Google.com.hk
从Cache中取值....
Person{名字='hopg', 年纪=123}
从Cache中取值....
Person{名字='hopg', 年纪=123}
执行其他操作,查询该值
Google.com.hk
从Cache中取值....
Person{名字='tSun', 年纪=11}
从Cache中取值....
Person{名字='tSun', 年纪=11}
执行其他操作,查询该值
Google.com.hk
从Cache中取值....
Google.com.hk
从Cache中取值....
Google.com.hk
执行其他操作,查询该值
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接口

 import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheBuilderSpec;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper; import java.io.Serializable;
import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-22 15:47
* Version: default 1.0.0
* Class description:
* <p/>
* *********************************************************
*/
public class GuavaCache implements Cache { private static final Object NULL_HOLDER = new NullHolder(); private final String name; private final com.google.common.cache.Cache<Object, Object> store; private final boolean allowNullValues; /**
* Create a new GuavaCache with the specified name.
*
* @param name the name of the cache
*/
public GuavaCache(String name) {
this(name, CacheBuilder.newBuilder(), true);
} /**
* Create a new GuavaCache with the specified name.
*
* @param name the name of the cache
* @param allowNullValues whether to accept and convert null values for this cache
*/
public GuavaCache(String name, boolean allowNullValues) {
this(name, CacheBuilder.newBuilder(), allowNullValues);
} /**
* Create a new GuavaCache using the specified name and {@link com.google.common.cache.CacheBuilderSpec specification}
*
* @param name the name of the cache
* @param spec the cache builder specification to use to build he cache
*/
public GuavaCache(String name, CacheBuilderSpec spec, boolean allowNullValues) {
this(name, CacheBuilder.from(spec), allowNullValues);
} /**
* Create a new GuavaCache using the specified name and {@link CacheBuilderSpec specification}
*
* @param name the name of the cache
* @param builder the cache builder to use to build the cache
*/
public GuavaCache(String name, CacheBuilder<Object, Object> builder, boolean allowNullValues) {
this.name = checkNotNull(name, "name is required");
this.allowNullValues = allowNullValues;
this.store = builder.maximumSize(CacheConstant.defaultCacheSize).
expireAfterWrite(CacheConstant.defaultCacheExpire, TimeUnit.MINUTES).
build();
} @Override
public String getName() {
return this.name;
} @Override
public com.google.common.cache.Cache<Object, Object> getNativeCache() {
return this.store;
} @Override
public ValueWrapper get(Object key) {
Object value = this.store.getIfPresent(key);
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
} @Override
public void put(Object key, Object value) {
this.store.put(key, value);
} /**
* remove the key of object
*
* @param key
*/
@Override
public void evict(Object key) {
this.store.invalidate(key);
} /**
* clear all
*/
@Override
public void clear() {
this.store.invalidateAll();
} /**
* Convert the given value from the internal store to a user value
* returned from the get method (adapting {@code null}).
*
* @param storeValue the store value
* @return the value to return to the user
*/
protected Object fromStoreValue(Object storeValue) {
if (this.allowNullValues && storeValue == NULL_HOLDER) {
return null;
}
return storeValue;
} /**
* Convert the given user value, as passed into the put method,
* to a value in the internal store (adapting {@code null}).
*
* @param userValue the given user value
* @return the value to store
*/
protected Object toStoreValue(Object userValue) {
if (this.allowNullValues && userValue == null) {
return NULL_HOLDER;
}
return userValue;
} @SuppressWarnings("serial")
private static class NullHolder implements Serializable { }
}

2.实现Spring的FactoryBean,BeanAware,InitializingBean接口

 import com.google.common.cache.CacheBuilder;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-22 16:00
* Version: default 1.0.0
* Class description:
* <p>{@link FactoryBean} for easy configuration of a {@link GuavaCache}.</p>
* <p/>
* *********************************************************
*/
public class GuavaCacheFactoryBean
implements FactoryBean<GuavaCache>, BeanNameAware, InitializingBean { private String name = ""; private boolean allowNullValues = true; private String spec; private GuavaCache cache; public void setName(String name) {
this.name = name;
} public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
} public void setSpec(String spec) {
this.spec = spec;
} @Override
public void setBeanName(String name) {
if (!StringUtils.hasLength(this.name)) {
this.name = name;
}
} @Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.hasText(this.spec)) {
this.cache = new GuavaCache(this.name, CacheBuilder.from(spec), allowNullValues);
} else {
this.cache = new GuavaCache(this.name, allowNullValues);
}
} @Override
public GuavaCache getObject() throws Exception {
return this.cache;
} @Override
public Class<?> getObjectType() {
return GuavaCache.class;
} @Override
public boolean isSingleton() {
return true;
} }

3.实现Spring的Manager类

 import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import org.springframework.util.StringUtils; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-22 16:09
* Version: default 1.0.0
* Class description:
* <p> {@link org.springframework.cache.CacheManager} implementation backed by {@link GuavaCache}.</p>
* <p/>
* *********************************************************
*/
public class GuavaCacheManager extends AbstractTransactionSupportingCacheManager {
private Collection<GuavaCache> caches; private String spec; private volatile CacheBuilder<Object, Object> cacheBuilder; private boolean allowNullValues = true; public GuavaCacheManager() {
} public void setCaches(Collection<GuavaCache> caches) {
this.caches = caches;
} public void setSpec(String spec) {
this.spec = spec;
} public String getSpec() {
return spec;
} public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
} public boolean isAllowNullValues() {
return allowNullValues;
} @Override
protected Collection<? extends Cache> loadCaches() {
return (caches != null) ? caches : Collections.<GuavaCache>emptyList();
} @Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
if (cache == null) {
// create a new cache
cache = createGuavaCache(name); // add to collection of available caches
addCache(cache);
}
return cache;
} private GuavaCache createGuavaCache(String name) {
// create GuavaCache
return new GuavaCache(name, getCacheBuilder(), allowNullValues);
} private CacheBuilder<Object, Object> getCacheBuilder() {
if (cacheBuilder == null) {
synchronized (this) {
if (cacheBuilder == null) {
if (StringUtils.hasText(spec)) {
cacheBuilder = CacheBuilder.from(spec);
} else {
cacheBuilder =CacheBuilder.newBuilder();
} }
notify();
}
} return cacheBuilder;
} }

4.3 配置spring配置文件applicationContext.xml

     <!--添加Cache-->
<!--添加一个注解驱动不要掉-->
<tx:annotation-driven/>
<!--使用spring注解去扫描需要加缓存地方的的包-->
<context:component-scan base-package="com.qunar.data.allinone.bus.testModel">
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
</context:component-scan>
<!-- cache Manager-->
<bean id="cacheManager" class="com.qunar.data.allinone.bus.cache.GuavaCacheManager">
<property name="caches">
<list>
<bean class="com.qunar.data.allinone.bus.cache.GuavaCacheFactoryBean" name="msg-cache"/>
</list>
</property>
</bean>
<!--cache的注解驱动包-->
<cache:annotation-driven/>

测试即可

 import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-22 19:50
* Version: default 1.0.0
* Class description:
* <p/>
* *********************************************************
*/
@Component
public class TestName { @Cacheable(value = "msg-cache")
public String getName(String con) {
System.out.println("缓存中没有找到信息");
return con;
}
}
 import com.qunar.data.allinone.bus.testModel.TestName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /**
* *********************************************************
* <p/>
* Author: XiJun.Gong
* Date: 2016-08-22 19:30
* Version: default 1.0.0
* Class description:
* <p/>
* *********************************************************
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class CacheTest { @Resource TestName testName; @Test
public void testName() {
String username = "xijun.gong";
for (int i = 0; i < 10; i++) {
System.out.println("++++++++++++++++打印结果: " + testName.getName(username));
}
}
}
缓存中没有找到信息
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二
++++++++++++++++打印结果: 王小二

5. 扩展

在github上看到一篇关于,对于overflow时候,将数据写入到文件系统的例子,还不错,如果有这方面的需求可以看看.

地址:https://github.com/raphw/guava-cache-overflow-extension