本地版说明
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
主启动类,开启Spring Cache
package gk.springboot.preheat;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
@MapperScan(basePackages = "gk.springboot.preheat.dao")
public class SpringBootPreheatApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPreheatApplication.class, args);
}
}
基于Caffeine本地缓存 或 Redis实现
- Caffeine是SpringBoot 2.x后支持的本地缓存,不再支持Guava
- CacheManager中有一个属性cacheMap,将数据存储在ConcurrentHashMap中(即JVM中)
- 这也是为什么下面配置caffeineCacheManager时,将缓存key和value设置为弱引用(可以防止缓存不过期导致的OOM)
public class CaffeineCacheManager implements CacheManager {
private Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
@Nullable
private CacheLoader<Object, Object> cacheLoader;
private boolean allowNullValues = true;
private boolean dynamic = true;
private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private final Collection<String> customCacheNames = new CopyOnWriteArrayList<>();
package gk.springboot.preheat.config;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@Configuration
public class CacheManagerConfig {
private long expire = 5;
private int initialCapacity = 16;
private long maximumSize = 1024;
@Bean("caffeineCacheManager")
@Primary
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(
Caffeine.newBuilder()
.expireAfterWrite(expire, TimeUnit.MINUTES)
.initialCapacity(initialCapacity)
.maximumSize(maximumSize)
.weakKeys()
.weakValues()
);
return cacheManager;
}
@Bean("redisCacheManager")
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofMinutes(expire))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
}
}
测试并查看
package gk.springboot.preheat.controller;
import gk.springboot.preheat.model.UserInfo;
import gk.springboot.preheat.service.UserService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user/")
public class UserController {
@Resource
private UserService userService;
@GetMapping("getById")
@Cacheable(value = "users",key = "#id",cacheManager = "caffeineCacheManager")
public UserInfo getUserById(@RequestParam("id") Integer id) {
System.out.println("未命中缓存,调用userService方法");
return userService.getUserById(id);
}
@GetMapping("getById2")
@Cacheable(value = "users",key = "#id",cacheManager = "redisCacheManager")
public UserInfo getUserById2(@RequestParam("id") Integer id) {
System.out.println("未命中缓存,调用userService方法");
return userService.getUserById(id);
}
}
- 通过请求 getById2后,可以在redis中看到被缓存的数据
- 请求getById后,未命中Caffeine本地缓存,则走数据库,否则从JVM中获取数据