本文来源:http://www.mydlq.club/article/55/
. 五、SpringCache 操作缓存的不足
. 六、SpringBoot + SpringCache + Redis 示例项目
. 1、Mavne 引入相关依赖
. 2、配置连接 Redis 参数
. 3、配置 Spring 缓存管理器
. 4、定义实体类
. 5、定义服务接口
. 6、实现服务类
. 7、创建 Controller
. 8、启动类
使用 Spring Cache 虽然方便,但是也有很多局限性,因为它多是根据请求参数命名 key,根据返回指设置 value,这样很多情况下,我们想方法内部进行命名和操作有一定的限制。如果我们需要灵活设置缓存,可以不用 SpringCache 提供的注解,直接在代码中使用 Spring-data-redis 包提供的方法,手动操作 key 与 value。
opsForValue().set(String key, String value);
opsForValue().get(String key);
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void redisBatch(){
// 设置值
redisTemplate.opsForValue().set("key", "value");
// 获取值
redisTemplate.opsForValue().get("key");
}
还有经常要批量设置、读取缓存,可以使用:
opsForValue().multiSet(Map map);
opsForValue().multiGet(List list);
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void redisBatch(){
// 批量设置值
Map<String,Object> map = new HashMap<>();
map.put("test1","value1");
map.put("test2","value2");
map.put("test3","value3");
redisTemplate.opsForValue().multiSet(map);
// 批量获取值,如果某个 key 不存在,则返回值集合中对于的为 null
List<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
list.add("test3");
List<Object> valueList = redisTemplate.opsForValue().multiGet(list);
}
下面是一个简单的 SpringBoot 项目,用于对用户的增删改查,这里使用 SpringCache 来模拟对数据进行缓存,示例如下:
Maven 中引入 SpringBoot 和 Redis 依赖,因为使用了
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>mydlq.club</groupId>
<artifactId>springboot-redis-example</artifactId>
<version>0.0.1</version>
<name>springboot-redis-example</name>
<description>Demo project for Spring Boot Redis</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
timeout: 1000
password:
lettuce:
pool:
max-active: 20
max-wait: -1
min-idle: 0
max-idle: 10
缓存配置类,里面配置缓存管理器,配置缓存的全局过期时间、序列化等参数。
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.*;
import java.time.Duration;
/**
* Redis 配置类
*/
@Configuration
public class RedisConfig {
/**
* 配置缓存管理器
* @param factory Redis 线程安全连接工厂
* @return 缓存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 生成两套默认配置,通过 Config 对象即可对缓存进行自定义配置
RedisCacheConfiguration cacheConfig1 = RedisCacheConfiguration.defaultCacheConfig()
// 设置过期时间 10 分钟
.entryTtl(Duration.ofMinutes(10))
// 设置缓存前缀
.prefixKeysWith("cache:user:")
// 禁止缓存 null 值
.disableCachingNullValues()
// 设置 key 序列化
.serializeKeysWith(keyPair())
// 设置 value 序列化
.serializeValuesWith(valuePair());
RedisCacheConfiguration cacheConfig2 = RedisCacheConfiguration.defaultCacheConfig()
// 设置过期时间 30 秒
.entryTtl(Duration.ofSeconds(30))
.prefixKeysWith("cache:user_info:")
.disableCachingNullValues()
.serializeKeysWith(keyPair())
.serializeValuesWith(valuePair());
// 返回 Redis 缓存管理器
return RedisCacheManager.builder(factory)
.withCacheConfiguration("user", cacheConfig1)
.withCacheConfiguration("userInfo", cacheConfig2)
.build();
}
/**
* 配置键序列化
* @return StringRedisSerializer
*/
private RedisSerializationContext.SerializationPair<String> keyPair() {
return RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
}
/**
* 配置值序列化,使用 GenericJackson2JsonRedisSerializer 替换默认序列化
* @return GenericJackson2JsonRedisSerializer
*/
private RedisSerializationContext.SerializationPair<Object> valuePair() {
return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
}
}
用户实体类
User
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 用户实体
*/
@Data
public class User {
private String username;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
private String role;
}
用户信息实体类
UserInfo
import lombok.Data;
/**
* 用户信息实体
*/
@Data
public class UserInfo {
private String name;
private String sex;
private Integer age;
}
UserService
import mydlq.club.example.entity.User;
/**
* 用户业务接口
*/
public interface UserService {
/**
* 增加账户
*
* @param user 账户
*/
void addUser(User user);
/**
* 获取账户
*
* @param username 用户名
* @return 用户信息
*/
User getUserByUsername(String username);
/**
* 修改账户
*
* @param user 用户信息
* @return 用户信息
*/
User updateUser(User user);
/**
* 删除账户
* @param username 用户名
*/
void deleteByUsername(String username);
}
UserInfoService
import mydlq.club.example.entity.UserInfo;
/**
* 用户信息业务接口
*/
public interface UserInfoService {
/**
* 增加用户信息
*
* @param userInfo 用户信息
*/
void addUserInfo(UserInfo userInfo);
/**
* 获取用户信息
*
* @param name 姓名
* @return 用户信息
*/
UserInfo getByName(String name);
/**
* 修改用户信息
*
* @param userInfo 用户信息
* @return 用户信息
*/
UserInfo updateUserInfo(UserInfo userInfo);
/**
* 删除用户信息
* @param name 姓名
*/
void deleteByName(String name);
}
实现 UserService 与 UserInfoService 接口中的方法,里面使用 @Cacheable
、 @CachePut
、 @CacheEvict
三个注解完成对用户与用户信息数据的缓存。
UserServiceImpl(用户业务实现类)
注意,为了演示方便,没有连接数据库,临时创建了个成员变量 userMap 来模拟数据库存储。
import mydlq.club.example.entity.User;
import mydlq.club.example.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@Service
@CacheConfig(cacheNames = "user")
public class UserServiceImpl implements UserService {
private HashMap<String, User> userMap = new HashMap<>();
@Override
public void addUser(User user) {
userMap.put(user.getUsername(), user);
}
@Override
@Cacheable(key = "#username",unless = "#result==null ")
public User getUserByUsername(String username) {
if (!userMap.containsKey(username)) {
return null;
}
return userMap.get(username);
}
@Override
@CachePut(key = "#user.username")
public User updateUser(User user) {
if (!userMap.containsKey(user.getUsername())){
throw new RuntimeException("不存在该用户");
}
// 获取存储的对象
User newUser = userMap.get(user.getUsername());
// 复制要更新的数据到新对象,因为不能更改用户名信息,所以忽略
BeanUtils.copyProperties(user, newUser, "username");
// 将新的对象存储,更新旧对象信息
userMap.put(newUser.getUsername(), newUser);
// 返回新对象信息
return newUser;
}
@Override
@CacheEvict(key = "#username")
public void deleteByUsername(String username) {
userMap.remove(username);
}
}
UserInfoServiceImpl(用户信息业务实现)
注意,为了演示方便,没有连接数据库,临时创建了个成员变量 userInfoMap 来模拟数据库存储。
import mydlq.club.example.entity.UserInfo;
import mydlq.club.example.service.UserInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@Service
@CacheConfig(cacheNames = "userInfo")
public class UserInfoServiceImpl implements UserInfoService {
private HashMap<String, UserInfo> userInfoMap = new HashMap<>();
@Override
public void addUserInfo(UserInfo userInfo) {
userInfoMap.put(userInfo.getName(), userInfo);
}
@Override
@Cacheable(key = "#name", unless = "#result==null")
public UserInfo getByName(String name) {
if (!userInfoMap.containsKey(name)) {
return null;
}
return userInfoMap.get(name);
}
@Override
@CachePut(key = "#userInfo.name")
public UserInfo updateUserInfo(UserInfo userInfo) {
if (!userInfoMap.containsKey(userInfo.getName())) {
throw new RuntimeException("该用户信息没有找到");
}
// 获取存储的对象
UserInfo newUserInfo = userInfoMap.get(userInfo.getName());
// 复制要更新的数据到新对象,因为不能更改用户名信息,所以忽略
BeanUtils.copyProperties(userInfo, newUserInfo, "name");
// 将新的对象存储,更新旧对象信息
userInfoMap.put(newUserInfo.getName(), newUserInfo);
// 返回新对象信息
return newUserInfo;
}
@Override
@CacheEvict(key = "#name")
public void deleteByName(String name) {
userInfoMap.remove(name);
}
}
UserController
import mydlq.club.example.entity.User;
import mydlq.club.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 用户 Controller
*/
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{username}")
public User getUser(@PathVariable String username) {
return userService.getUserByUsername(username);
}
@PostMapping("/user")
public String createUser(@RequestBody User user) {
userService.addUser(user);
return "SUCCESS";
}
@PutMapping("/user")
public User updateUser(@RequestBody User user) {
return userService.updateUser(user);
}
@DeleteMapping("/user/{username}")
public String deleteUser(@PathVariable String username) {
userService.deleteByUsername(username);
return "SUCCESS";
}
}
UserInfoController
import mydlq.club.example.entity.UserInfo;
import mydlq.club.example.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 用户信息 Controller
*/
@RestController
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
@GetMapping("/userInfo/{name}")
public UserInfo getUserInfo(@PathVariable String name) {
return userInfoService.getByName(name);
}
@PostMapping("/userInfo")
public String createUserInfo(@RequestBody UserInfo userInfo) {
userInfoService.addUserInfo(userInfo);
return "SUCCESS";
}
@PutMapping("/userInfo")
public UserInfo updateUserInfo(@RequestBody UserInfo userInfo) {
return userInfoService.updateUserInfo(userInfo);
}
@DeleteMapping("/userInfo/{name}")
public String deleteUserInfo(@PathVariable String name) {
userInfoService.deleteByName(name);
return "SUCCESS";
}
}
启动类中添加 @EnableCaching
注解开启缓存。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableCaching
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Go to "Discover" > "Top Stories" > "Wow"