與Redis相關知識可查看《Redis系列記錄》
一、相關配置
1.1 pom.xml
<!-- spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!-- 連接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
1.2 application.properties
spring.cache.type=redis
# Redis數據庫索引(默認爲0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=localhost
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認爲空)
#spring.redis.password=123456
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.lettuce.max-active=200
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.lettuce.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.lettuce.max-idle=10
# 連接池中的最小空閒連接
spring.redis.lettuce.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=1000ms
logging.level.com.example.redisdemo=debug
1.3 RedisConfig
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisSerializer fastJson2JsonRedisSerialize(){
return new FastJson2JsonRedisSerialize<Object>(Object.class);
}
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,RedisSerializer fastJson2JsonRedisSerialize){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//設置Key的序列化採用StringRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//設置值的序列化採用FastJsonRedisSerializer
redisTemplate.setValueSerializer(fastJson2JsonRedisSerialize);
redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerialize);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 生成一個默認配置,通過config對象即可對緩存進行自定義配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 設置緩存的默認過期時間,也是使用Duration設置
config = config.entryTtl(Duration.ofMinutes(5))
// 設置 key爲string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 設置value爲fastJson序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJson2JsonRedisSerialize()))
// 不緩存空值
.disableCachingNullValues();
// 使用自定義的緩存配置初始化一個cacheManager
return RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
}
1.4 FastJson2JsonRedisSerialize
public class FastJson2JsonRedisSerialize<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//如果遇到反序列化autoType is not support錯誤,請添加並修改一下包名到bean文件路徑
//ParserConfig.getGlobalInstance().addAccept("com.example.redisdemo.domain");
}
public FastJson2JsonRedisSerialize(Class clazz){
super();
this.clazz = clazz;
}
/**
* 序列化
* @param t
* @return
* @throws SerializationException
*/
@Override
public byte[] serialize(T t) throws SerializationException {
if (null == t){
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
/**
* 反序列化
* @param bytes
* @return
* @throws SerializationException
*/
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (null == bytes || bytes.length <= 0){
return null;
}
String str = new String(bytes,DEFAULT_CHARSET);
return (T) JSON.parseObject(str,clazz);
}
}
二、測試
2.1 User
public class User implements Serializable {
private static final long serialVersionUID = 8391377903506428591L;
private Integer id;
private String username;
private String password;
public User(){
super();
}
public User(Integer id, String username, String password){
this.id = id;
this.username = username;
this.password = password;
}
getter... setter... toString()...
}
2.2 UserService UserServiceImpl
public interface UserService {
public User addUser(User user);
public User getUser(Integer id);
public void delUser(Integer id);
}
這裏不用Mapper了,使用Map來模擬一下數據庫操作,
@CacheConfig(cacheNames = "user")
@Service("userService")
public class UserServiceImpl implements UserService {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
private static final Map<String,Object> usersMap = new HashMap<>();
@Override
@CachePut(key = "#user.id")
public User addUser(User user) {
usersMap.put(user.getId().toString(),user);
logger.info("存入數據庫【User】= {}",user);
return user;
}
@Override
@Cacheable(key = "#id",unless = "#result == null ")
public User getUser(Integer id){
User user = (User) usersMap.get(id.toString());
logger.info("從數據庫獲取【User】= {}",user);
return user;
}
@Override
@CacheEvict(key = "#id")
public void delUser(Integer id) {
usersMap.remove(id.toString());
logger.info("從數據庫刪除【User】");
}
}
2.3 Test
@Test
void testOne() {
userService.addUser(new User(1,"aa","123"));
User user = userService.getUser(1);
logger.debug("【User】={}",user);
}
2.4 結果
這裏可以看到只打印了添加日誌和最後的查詢結果,並沒有打印查詢日誌,所以證明緩存生效。
三、相關知識點與坑
3.1 報com.alibaba.fastjson.JSONException: autoType is not support
解決方法:在FastJson2JsonRedisSerialize 類中添加 這兩句代碼都可以解決反序列化問題
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//如果遇到反序列化autoType is not support錯誤,請添加並修改一下包名到bean文件路徑
//ParserConfig.getGlobalInstance().addAccept("com.example.redisdemo.domain");
}
3.2 Cache 'redisCache' does not allow 'null' values
解決方法:添加unless = "#result == null"
@Cacheable(key = "#id",unless = "#result == null ")
3.3 @CacheConfig
此註解是爲了抽取緩存註解中公共的配置
3.4 @Cacheable
可用於類或方法上;
在目標方法執行前,會根據key先去緩存中查詢看是否有數據,
有就直接返回緩存中的key對應的value值。不再執行目標方法;
無則執行目標方法,並將方法的返回值作爲value,並以鍵值對的形式存入緩存
3.5 @CachePut
應用到寫數據的方法上,如新增/修改方法,
在執行完目標方法後,並將方法的返回值作爲value,並以鍵值對的形式存入緩存中
3.6 @CacheEvict
應用到刪除數據的方法上,
在執行完目標方法後,清除緩存中對應key的數據(如果緩存中有對應key的數據緩存的話)
3.7 @EnableCaching
開關性註解,在項目啓動類或某個配置類上使用此註解後,則表示允許使用註解的方式進行緩存操作
華麗分隔符
================================================================================================
上文中使用註解來完成緩存操作,下面再來一種方式
在測試類中添加:
@Resource(name = "redisTemplate")
private ValueOperations<String,Object> vo;
@Test
void testFour(){
vo.set("java",new User(1,"bb","111"));
User user = (User) vo.get("java");
System.out.println(user);
}
執行: 也是好用的
那麼爲什麼redisTemplate可以直接注入給五種數據類型的XXOperations使用呢?原來有屬性編輯器
此篇內容後期還有擴充,未完待續...