都有哪些Spring Cache
我们现在在用的Spring Cache,可以直接看Spring Boot提供的缓存枚举类,有如下这些:
public enum CacheType {
GENERIC, // 使用的SimpleCacheManager(自己手动指定Cache,可任意类型Cache实现哦)
JCACHE, // 使用org.springframework.cache.jcache.JCacheCacheManager
EHCACHE, // 使用org.springframework.cache.ehcache.EhCacheCacheManager
HAZELCAST, // 使用com.hazelcast.spring.cache.HazelcastCacheManager
INFINISPAN, // 使用org.infinispan.spring.provider.SpringEmbeddedCacheManager
COUCHBASE, // 使用com.couchbase.client.spring.cache.CouchbaseCacheManager
REDIS, // 使用org.springframework.data.redis.cache.RedisCacheManager,依赖于RedisTemplate进行操作
CAFFEINE, // 使用org.springframework.cache.caffeine.CaffeineCacheManager
@Deprecated
GUAVA, // 使用org.springframework.cache.guava.GuavaCacheManager,已经过期不推荐使用了
SIMPLE, // 使用ConcurrentMapCacheManager
NONE; // 使用NoOpCacheManager,表示禁用缓存
}
EhCache:一个纯Java的进程内缓存框架,所以也是基于本地缓存的。(注意EhCache2.x和EhCache3.x相互不兼容)。
Redis:分布式缓存,只有Client-Server(C\S)模式,Java一般使用Jedis/Luttuce来操纵。
Hazelcast:基于内存的数据网格。虽然它基于内存,但是分布式应用程序可以使用Hazelcast进行分布式缓存、同步、集群、处理、发布/订阅消息等。
Guava:它是Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU(最近最少使用)算法实现,支持多种缓存过期策略。在Spring5.X以后的版本已经将他标记为过期了。
Caffeine:是使用Java8对Guava缓存的重写版本,在Spring5中将取代了Guava,支持多种缓存过期策略。
SIMPLE:使用ConcurrentMapCacheManager,因为不支持缓存过期时间,所以做本地缓存基本不考虑该方式。
本地缓存的性能
关于分布式缓存,我们需要后面会专门讨论Redis的用法,这里只看本地缓存。性能从高到低,依次是Caffeine,Guava,ConcurrentMapCacheManager,其中Caffeine在读写上都快了Guava近一倍。
本地缓存的用法
这里我们只讨论在Spring Boot里面怎么整合使用Caffeine和EhCache。
Caffeine的整合使用
主要有以下几个步骤:
1)加依赖包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
2)配置缓存:
这里有两种方法,通过文件配置或者在配置类里面配置,先看一下文件配置,我们可以写一个properties文件,内容像这样:
spring:
cache:
type: CAFFEINE
cache-names:
- getPersonById
- name2
caffeine:
spec: maximumSize=500,expireAfterWrite=5s
然后还要在主类中加上@EnableCaching注解:
@SpringBootApplication
@EnableScheduling
@EnableCaching
public class MySpringBootApplication
另外一种更灵活的方法是在配置类中配置:
package com.xjj.config;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.github.benmanes.caffeine.cache.Caffeine;
/**
* Cache配置类,用于缓存数据
*/
@Configuration
@EnableCaching
public class CacheConfig {
public static final int DEFAULT_MAXSIZE = 50000;
public static final int DEFAULT_TTL = 10;
/**
* 定義cache名称、超时时长(秒)、最大容量
* 每个cache缺省:10秒超时、最多缓存50000条数据,需要修改可以在构造方法的
参数中指定。
*/
public enum Caches{
getPersonById(5), //有效期5秒
getSomething, //缺省10秒
getOtherthing(300, 1000), //5分钟,最大容量1000
Caches() {
}
Caches(int ttl) {
this.ttl = ttl;
}
Caches(int ttl, int maxSize) {
this.ttl = ttl;
this.maxSize = maxSize;
}
private int maxSize=DEFAULT_MAXSIZE; //最大数量
private int ttl=DEFAULT_TTL; //过期时间(秒)
public int getMaxSize() {
return maxSize;
}
public int getTtl() {
return ttl;
}
}
/**
* 创建基于Caffeine的Cache Manager
* @return
*/
@Bean
@Primary
public CacheManager caffeineCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();
for(Caches c : Caches.values()){
caches.add(new CaffeineCache(c.name(),
Caffeine.newBuilder().recordStats().expireAfterWrite(c.getTtl(),
TimeUnit.SECONDS).maximumSize(c.getMaxSize()).build()));
}
cacheManager.setCaches(caches);
return cacheManager;
}
}
应用类:
package com.xjj.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.xjj.dao.PersonDAO;
import com.xjj.entity.Person;
@Service
public class PersonService {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private PersonDAO personDao;
/**
* 根据id获取Person对象,使用缓存
* @param id
* @return Person对象
*/
@Cacheable(value="getPersonById", sync=true)
public Person getPersonById(int id){
logger.debug("getting data from database, personId={}", id);
return personDao.getPersonById(id);
}
}
测试类:
@Autowired
PersonService personService;
@Test
public void localCacheTest() throws JsonProcessingException, InterruptedException{
System.out.println("第一次:"); //从数据库中获取
Person p = personService.getPersonById(2);
logger.info("1st time: {}", objectMapper.writeValueAsString(p));
System.out.println("第二次:"); //从缓存中获取
p = personService.getPersonById(2);
logger.info("2nd time: {}", objectMapper.writeValueAsString(p));
Thread.sleep(5000);
System.out.println("第三次:"); //5秒钟超时后,从数据库中获取
p = personService.getPersonById(2);
logger.info("3rd time: {}", objectMapper.writeValueAsString(p));
System.out.println("第四次:"); //从缓存中获取
p = personService.getPersonById(2);
logger.info("4th time: {}", objectMapper.writeValueAsString(p));
}
ehcache的整合使用
导入依赖包,分为2.x版本和3.x版本。
其中2.x版本做如下导入:
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
3.x版本做如下导入:
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.7.1</version>
</dependency>
导包完成后,我们使用JCacheManagerFactoryBean + ehcache.xml的方式配置:
@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Bean
public JCacheManagerFactoryBean cacheManagerFactoryBean() throws IOException {
JCacheManagerFactoryBean factoryBean = new JCacheManagerFactoryBean();
// 配置全部写在ehcache.xml这个配置文件内
factoryBean.setCacheManagerUri(new ClassPathResource("ehcache.xml").getURI());
return factoryBean;
}
@Bean
public CacheManager cacheManager(javax.cache.CacheManager cacheManager) {
// 它必须要包装一个javax.cache.CacheManager,也就是Eh107CacheManager才行
JCacheCacheManager cacheCacheManager = new JCacheCacheManager();
// 方式一:使用`JCacheManagerFactoryBean` + xml配置文件的方式
cacheCacheManager.setCacheManager(cacheManager);
return cacheCacheManager;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.1.xsd">
<!-- <service>
<jsr107:defaults>
<jsr107:cache name="demoCache" template="heap-cache"/>
</jsr107:defaults>
</service> -->
<cache-template name="heap-cache">
<resources>
<heap unit="entries">2000</heap>
<offheap unit="MB">100</offheap>
</resources>
</cache-template>
<!-- 注意:这个cache必须手动配置上,它并不会动态生成 -->
<cache alias="demoCache" uses-template="heap-cache">
<expiry>
<ttl unit="seconds">40</ttl>
</expiry>
</cache>
</config>
参考资料:
https://blog.csdn.net/f641385712/article/details/94982916
http://www.360doc.com/content/17/1017/20/16915_695800687.shtml