文章目录
JSR107规范
Spring缓存抽象
SpringBoot中的默认缓存
@Cacheable及其属性
- 使用
@EnableCaching
开启缓存 - 属性及含义
属性名 | 含义及作用 |
---|---|
cacheName/value | 指定缓存组件的名字 |
key | 缓存数据时用的key,若不指定则默认使用方法参数值。value为方法返回值。 |
keyGenerator | key的生成器。可以自己指定key的生成器的组件id。key与keyGenerator二者指定其一。 |
cacheManager | 指定缓存管理器,或指定cacheResolver |
condition | 指定符合条件的情况下进行缓存 |
unless | 指定的条件为true时,不会进行缓存,可以获取到结果进行判断 |
sync | 是否使用异步 |
@Cacheable示例
在 SpringBoot入门学习笔记(七)SpringBoot数据访问 的基础上搭建。
- 给SpringBoot入口程序加上注解
@EnableCaching
@SpringBootApplication
@EnableCaching //开启缓存
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
- 创建数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
- 创建实体类
@Entity
@Table(name = "user")
public class User {
@Id //这是主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增列
private Integer id;
@Column(name = "name",length = 255)
private String name;
@Column //若省略则默认列名为属性名
private String pwd;
//省略getter、setter、toString方法
}
- 编写Service层
package com.hx.cache.service;
import com.hx.cache.entity.User;
import com.hx.cache.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* @ClassPath: com.hx.cache.service.UserService
* @Author: Huathy
* @Description:
* @Date: 2020-09-20 23:21
*/
@Service
public class UserService {
@Autowired
UserRepository userRepository;
/**
* 将该方法的运行结果进行缓存,以后要相同的数据,直接到缓存中获取。不用调用方法
*
* cacheManager:管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中。每个缓存组件有自己唯一的名字
* 几个属性:
* cacheName/value:指定缓存组件的名字
* key:缓存数据时用的key,若不指定则默认使用方法参数值。value为方法返回值。
* keyGenerator:key的生成器。可以自己指定key的生成器的组件id。key与keyGenerator二者指定其一。
* cacheManager:指定缓存管理器,或指定cacheResolver
* condition:指定符合条件的情况下进行缓存
* unless:指定的条件为true时,不会进行缓存,可以获取到结果进行判断
* sync:是否使用异步
*/
@Cacheable(cacheNames = "user",key = "#id",condition = "#id>0",unless = "#result==null")
public Optional<User> getUser(Integer id){
System.out.println("getUser Executed...");
Optional<User> user = userRepository.findById(id);
return user;
}
}
- Controller层
package com.hx.cache.controller;
import com.hx.cache.entity.User;
import com.hx.cache.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
/**
* @ClassPath: com.hx.jpa.controller.UserController
* @Author: Huathy
* @Description:
* @Date: 2020-09-20 15:34
*/
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/getUser/{id}")
//Optional类是Java8为了解决null值判断问题,可避免null导致的NPE(空指针异常)
public Optional<User> getUser(@PathVariable("id") Integer id){
return userService.getUser(1);
}
@RequestMapping("/add")
public User add(User user){
User u = userService.add(user);
return u;
}
}
- repository
package com.hx.cache.repository;
import com.hx.cache.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* 继承JpaRepository来完成对数据库的操作
*/
public interface UserRepository extends JpaRepository<User,Integer> {
}
- 配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/jpaTest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: a
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
show-sql: true #控制台显示sql
hibernate:
#更新或创建数据表结构
ddl-auto: update
- 测试:访问
localhost:8080/getUser/1
。当第一次访问时,调用了Service层的查询方法。而第二次访问时,该数据从缓存中读取。
属性KeyGenerator示例
- 编写配置类
package com.hx.cache.config;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @ClassPath: com.hx.cache.config.MyConfig
* @Author: Huathy
* @Description:
* @Date: 2020-09-23 18:48
*/
@Configuration
public class CacheConfig {
@Bean(name = "myKeyGenerator") //指定该组件的id
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName() + "[" + Arrays.asList(params).toString() + "]";
}
};
}
}
- 修改Service层
@Cacheable(cacheNames = "user",keyGenerator = "myKeyGenerator")
public Optional<User> getUser(Integer id){
System.out.println("getUser Executed...");
Optional<User> user = userRepository.findById(id);
return user;
}
- 测试debug 访问:
localhost:8080/getUser/1
@CachePut
先调用方法,再将目标结果进行缓存
示例
- Mapper映射接口
package com.hx.cache.mapper;
import com.hx.cache.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface UserMapper {
@Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
public int update(User user);
}
- Service层
@Service
public class UserService {
@Autowired
UserMapper userMapper;
@Cacheable(cacheNames = "user",key = "#id")
public Optional<User> getUser(Integer id){
System.out.println("getUser Executed...");
Optional<User> user = userRepository.findById(id);
return user;
}
/**
* @CachePut : 既调用方法又更新缓存
* 运行时机:先调用目标方法,再将目标结果进行缓存
* 这里需要注意key与上面的key相同,才可以更新
*/
@CachePut(value = "emp",key = "#u.id")
public int updateUser(User u){
System.out.println("UPDATE USER...");
return userMapper.update(u);
}
}
- Controller层
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/update")
public int update(User user){
return userService.updateUser(user);
}
}
@CacheEvict 缓存清除
示例如下:
- Service层
@Service
public class UserService {
@Autowired
UserRepository userRepository;
// @CacheEvict(value = "user",key = "#id") //删除指定数据
@CacheEvict(value = "user",allEntries = true) //删除user下的所有数据
public int del(Integer id){
userRepository.deleteById(id);
return 1;
}
}
- Controller层
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/delUser")
public int del(Integer id){
return userService.del(id);
}
}
- 测试:先查询,然后删除,再次查询可见缓存已被清除,查询数据库结果为null。
@Caching 定义复杂的缓存注解
示例如下:
- mapper映射接口
@Mapper
public interface UserMapper {
@Select("select * from user where name=#{name} limit 0,1")
public User getUserByName(String name);
}
- Service层
@Service
public class UserService {
@Autowired
UserMapper userMapper;
/**
* @Caching :定义复杂的缓存注解
*/
@Caching(
cacheable = {
@Cacheable(value = "user",key = "#name")
},
put = {
@CachePut(value = "user",key = "#result.id"),
}
)
public User getUserByName(String name){
return userMapper.getUserByName(name);
}
}
- controller层
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/getUserByName")
public User getUserByName(String name){
return userService.getUserByName(name);
}
}
@CacheConfig 缓存配置
作用:用于配置缓存配置中的公共部分
示例如下:
/**
* @CacheConfig : 缓存配置注解,配置缓存中的公共配置
* cacheNames属性 :可省去下边的value="user"
*/
@CacheConfig(cacheNames = "user")
@Service
public class UserService {
//省略代码......
//如以下代码中的cacheNames、value可以省略
@Cacheable(cacheNames = "user",key = "#id")
@CacheEvict(value = "user",allEntries = true)
}
SpringBoot使用Redis缓存
下载安装Redis缓存
下载地址:http://download.redis.io/releases/redis-6.0.6.tar.gz
redis中文网:http://www.redis.cn/
redis桌面工具:https://redisdesktop.com/
在SpringBoot中使用redis
- 引入redis依赖、jedis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 编写测试类
@SpringBootTest
class CacheApplicationTests {
@Autowired
StringRedisTemplate stringRedisTemplate; //操作key-value都是字符串的
@Autowired
RedisTemplate redisTemplate; //操作key-value都是对象的
/**
* Redis五种常见的数据类型
* String,List,Set,ZSet,Hash
* stringRedisTemplate.opsForValue();
* stringRedisTemplate.opsForList();
* stringRedisTemplate.opsForSet();
* stringRedisTemplate.opsForZSet();
* stringRedisTemplate.opsForHash();
*/
@Test
void test01(){
stringRedisTemplate.opsForValue().append("msg","jiujiu");
}
/**
* 测试保存对象
* 默认保存对象会使用jdk序列化机制,将序列化后的对象保存到redis中
* 注意:这里需要实体类必须实现序列化接口
*/
@Test
void test02(){
Optional<User> users = userRepository.findById(5);
User user = users.get();
redisTemplate.opsForValue().set("user01",user);
}
}
使用自定义序列化
- 编写缓存的配置类
package com.hx.cache.config;
import com.hx.cache.entity.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.net.UnknownHostException;
/**
* @ClassPath: com.hx.cache.config.MyRedisConfig
* @Author: Huathy
* @Description:
* @Date: 2020-09-25 09:02
*/
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, User> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class);
template.setDefaultSerializer(serializer);
return template;
}
}
- 使用测试
@SpringBootTest
class CacheApplicationTests {
@Autowired
UserRepository userRepository;
@Autowired
RedisTemplate<Object,User> userRedisTemplate;
@Test
void test03(){
Optional<User> users = userRepository.findById(5);
User user = users.get();
userRedisTemplate.opsForValue().set("user02",user);
}
}
- 使用RedisDesktopManager查看可见缓存的数据
自定义CacheManager
- 引入Redis的starter后,容器中注入了RedisCacheManager。其他的CacheManager不起作用。
- RedisCacheManager帮我们创建RedisCache来作为缓存组件,RedisCache来操作redis缓存数据
- 默认保存数据key-value都是object,利用序列化保存
- 引入redis的starter,cacheManager变为RedisCacheManager
- 默认创建RedisCacheManager,操作redis时使用RedisTemplate<Object,Object>。其默认使用的是jdk序列化机制
- 为使用json序列化,需要自定义CacheManager
自定义CacheManager示例
- RedisConfig配置类
package com.hx.cache.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hx.cache.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.List;
/**
* @ClassPath: com.hx.cache.config.MyRedisConfig
* @Author: Huathy
* @Description:
* @Date: 2020-09-25 09:02
*/
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, User> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class);
template.setDefaultSerializer(serializer);
return template;
}
//这是Redis缓存管理器
@Bean
public RedisCacheManager userRedisCacheManager(RedisConnectionFactory connectionFactory){
Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
redisSerializer.setObjectMapper(objectMapper);
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();
return redisCacheManager;
}
}
注:本文示例代码均为关键部分代码。