SpringBoot整合Redis,Redis突然宕机,不影响业务逻辑正常运行

创建SpringBoot项目

运行docker,测试连接

docker run -itd --name dockerRedis -p 6379:6379 redis

docker exec -it dockerRedis redis-cli

// 查看所有容器
docker ps -a
// 查看所有容器ID
docker ps -a -q
// stop停止所有容器
docker stop $(docker ps -a -q)
//remove删除所有容器
docker  rm $(docker ps -a -q)

官方创建:https://start.spring.io/

pom.xml文件中增加连接池

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-pool2</artifactId>
</dependency>

完整pom.xml文件为:

<?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.4.1</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.redis.learning.it</groupId>
	<artifactId>SpringBootRedisDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootRedisDemo</name>
	<description>Demo project for Spring Boot With Redis</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</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.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

配置文件application.properties:

server.port=8080
server.servlet.context-path=/redis

############################################################
# REDIS 配置
############################################################
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=2
spring.redis.timeout=100
spring.cache.cache-names=myCache

启动类上添加如下代码,表示开启缓存:

package org.redis.learning.it;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

// 开启缓存
@EnableCaching
@SpringBootApplication
public class SpringBootRedisDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRedisDemoApplication.class, args);
    }

}

准备User Bean文件:其中LocalDate序列化与反序列化配置

package org.redis.learning.it.bean;

import java.io.Serializable;
import java.time.LocalDate;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;

import lombok.Data;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = 7043831680764669930L;

    private int userId;
    private String userName;
    private int age;
	// LocalDate序列化与反序列化配置
	@JsonDeserialize(using = LocalDateDeserializer.class)
	@JsonSerialize(using = LocalDateSerializer.class)
	@JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthday;
}

备注:LocalDate和LocalDateTime序列化&反序列化示例

package com.lwy.it.book.vo;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;

import lombok.Data;

@Data
public class BookVO implements Serializable {

	private static final long serialVersionUID = 683534413370900389L;
	private int id;
	private String bookName;
	private double bookPrice;
	
	// LocalDate序列化与反序列化配置
	@JsonDeserialize(using = LocalDateDeserializer.class)
	@JsonSerialize(using = LocalDateSerializer.class)
	@JsonFormat(pattern = "yyyy-MM-dd")
	private LocalDate bookPublishDate;

	// LocalDateTime序列化与反序列化配置
	@JsonDeserialize(using = LocalDateTimeDeserializer.class)
	@JsonSerialize(using = LocalDateTimeSerializer.class)
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private LocalDateTime bookProduceTime;

}

SpringBoot 整合 Redis,否则在Service中@Resource导入失败

package org.redis.learning.it.configuration;

import javax.annotation.Resource;

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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfiguration {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        template.setKeySerializer(keySerializer);
        template.setHashKeySerializer(keySerializer);
        // 使用GenericJackson2JsonRedisSerializer来序列化和反序列化Redis的Value/HashValue值(默认使用JDK的序列化方式)
        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(valueSerializer);
        template.setHashValueSerializer(valueSerializer);
        return template;
    }

    @Bean
    public RedisOperations<String, Object> redisOperations() {
        RedisOperations<String, Object> redisOperations = redisTemplate;
        return redisOperations;
    }

}

更新文件,重写public CacheErrorHandler errorHandler()方法:

使用GenericJackson2JsonRedisSerializer序列化和反序列化Value:

package org.redis.learning.it.configuration;

import javax.annotation.Resource;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
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.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        template.setKeySerializer(keySerializer);
        template.setHashKeySerializer(keySerializer);
        // 使用GenericJackson2JsonRedisSerializer来序列化和反序列化Redis的Value/HashValue值(默认使用JDK的序列化方式)
        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(valueSerializer);
        template.setHashValueSerializer(valueSerializer);
        return template;
    }

    @Bean
    public RedisOperations<String, Object> redisOperations() {
        RedisOperations<String, Object> redisOperations = redisTemplate;
        return redisOperations;
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return new RedisCacheErrorHandler();
    }
}

使用Jackson2JsonRedisSerializer序列化和反序列化Value: 

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
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 org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 配置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化Redis的value值
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定药序列化的域,field,get和set,以及修饰符范围
        // ANY任何级别的字段都可以自动识别
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // value采用Json序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 使用StringRedisSerializer来序列化和反序列化Redis的key
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 使用StringRedisSerializer来序列化和反序列化Redis的Hash key
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // Hash value采用Json序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return new RedisCacheErrorHandler();
    }
}

Spring CacheManager 配置

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheManager redisCacheManager =
                // 配置Redis连接工厂
                RedisCacheManager.builder(redisConnectionFactory)
                        // 设置默认配置
                        .cacheDefaults(defaultRedisCacheConfiguration(1200L))
                        // 不同Key的自定义配置
                        .withInitialCacheConfigurations(this.initialCacheConfiguration())
                        .transactionAware()
                        .build();
        return redisCacheManager;
    }

    private RedisCacheConfiguration defaultRedisCacheConfiguration(Long second) {
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化Redis的value值
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定药序列化的域,field,get和set,以及修饰符范围
        // ANY任何级别的字段都可以自动识别
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        RedisCacheConfiguration redisCacheConfiguration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofSeconds(second))
                        .serializeKeysWith(RedisSerializationContext
                                .SerializationPair
                                .fromSerializer(new StringRedisSerializer()))
                        .serializeValuesWith(RedisSerializationContext
                                .SerializationPair
                                .fromSerializer(jackson2JsonRedisSerializer))
                        .disableCachingNullValues();
        return redisCacheConfiguration;
    }

    private Map<String, RedisCacheConfiguration> initialCacheConfiguration() {
        Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>();
        // 设置redisCache 120s过期,同时设置序列化方式
        configurationMap.put("redisCache", this.defaultRedisCacheConfiguration(120L));
        // testCache 240s过期,同时设置序列化方式
        configurationMap.put("testCache", this.defaultRedisCacheConfiguration(240L));
        return configurationMap;
    }
}

新增RedisCacheErrorHandler处理Cache Exception

package org.redis.learning.it.configuration;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheErrorHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RedisCacheErrorHandler implements CacheErrorHandler {

    @Override
    public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
        log.error(exception.getMessage(), exception);
    }

    @Override
    public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
        log.error(exception.getMessage(), exception);
    }

    @Override
    public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
        log.error(exception.getMessage(), exception);
    }

    @Override
    public void handleCacheClearError(RuntimeException exception, Cache cache) {
        log.error(exception.getMessage(), exception);
    }

}

新增Redis操作Util:RedisOperateUtil,使得Redis宕机不影响业务逻辑:

package org.redis.learning.it.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class RedisOperateUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public boolean exists(String key) {
        boolean result = false;
        try {
            result = redisTemplate.hasKey(key);
        } catch (RedisConnectionFailureException exception) {
            log.error(exception.getMessage(), exception);
        } catch (QueryTimeoutException exception) {
            log.error(exception.getMessage(), exception);
        }
        return result;
    }

    public Object get(Object key) {
        Object object = null;
        try {
            object = redisTemplate.opsForValue().get(key);
        } catch (RedisConnectionFailureException exception) {
            log.error(exception.getMessage(), exception);
        } catch (QueryTimeoutException exception) {
            log.error(exception.getMessage(), exception);
        }
        return object;
    }

    public void set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception exception) {
            log.error(exception.getMessage(), exception);
        }
    }

}

RedisOperations及子类RedisTemplate使用:

package org.redis.learning.it.service;

import javax.annotation.Resource;

import org.redis.learning.it.bean.User;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

@Service
public class UserService {

    @Resource
    private RedisOperations<String, User> redisOperations;

    @Resource
    private RedisTemplate<String, User> redisTemplate;

    public User findUserById(int id) {
        User user = (User)redisOperations.opsForValue().get(md5(id));
        return user;
    }

    public void saveUser(User user) {
        redisTemplate.opsForValue().set(md5(user.getUserId()), user);
    }

    // 通过MD5自定义Key
    private String md5(int id) {
        String salt = "SpringBootWithRedis";
        String key = String.valueOf(id);
        String md5 = DigestUtils.md5DigestAsHex(key.concat(salt).getBytes());
        return md5;
    }

}

更新UserService:

package org.redis.learning.it.service;

import java.time.LocalDate;

import org.redis.learning.it.bean.User;
import org.redis.learning.it.util.RedisOperateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

@Service
public class UserService {

    @Autowired
    private RedisOperateUtil redisOperateUtil;

    public User findUserById(int id) {
        User user = (User)redisOperateUtil.get(md5(id));
        if (user == null) {
            // 模拟数据库查询操作等
            user = new User();
            user.setUserId(1);
            user.setUserName("张三");
            user.setAge(18);
            user.setBirthday(LocalDate.of(2000, 10, 10));
        }
        return user;
    }

    public void saveUser(User user) {
        redisOperateUtil.set(md5(user.getUserId()), user);
    }

    // 通过MD5自定义Key
    private String md5(int id) {
        String salt = "SpringBootWithRedis";
        String key = String.valueOf(id);
        String md5 = DigestUtils.md5DigestAsHex(key.concat(salt).getBytes());
        return md5;
    }

}

接口Controller使用@Cache缓存配置

package org.redis.learning.it.controller;

import java.time.LocalDate;

import org.redis.learning.it.bean.User;
import org.redis.learning.it.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CacheConfig(cacheNames = "myCache")
public class RedisController {

    @Autowired
    private UserService userService;

    @GetMapping("/saveUser")
    public String saveUser() {
        User user = new User();
        user.setUserId(1);
        user.setUserName("张三");
        user.setBirthday(LocalDate.of(1990, 01, 01));
        user.setAge(30);
        userService.saveUser(user);
        return "SUCCESS";
    }

    @GetMapping("/findUser")
    @Cacheable(key = "#id")
    public User findUserById(Integer id) {
        if (id != null) {
            User user = userService.findUserById(id);
            return user;
        }
        return new User();
    }

}

源码分析:RedisCacheManager,相关的配置是在org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration 类中完成的。部分源码如下:

@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
	@Bean
	public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
			ResourceLoader resourceLoader) {
		RedisCacheManagerBuilder builder = RedisCacheManager
				.builder(redisConnectionFactory)
				.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
		List<String> cacheNames = this.cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
		}
		return this.customizerInvoker.customize(builder.build());
	}
}

看类上的注解,发现在万事俱备的情况下,系统会自动提供一个 RedisCacheManager 的 Bean,这个 RedisCacheManager 间接实现了 Spring 中的 Cache 接口,有了这个 Bean,我们就可以直接使用 Spring 中的缓存注解和接口了,而缓存数据则会被自动存储到 Redis 上。

缓存使用

@CacheConfig

这个注解在类上使用,用来描述该类中所有方法使用的缓存名称,当然也可以不使用该注解,直接在具体的缓存注解上配置名称。

@Cacheable

这个注解一般加在查询方法上,表示将一个方法的返回值缓存起来,默认情况下,缓存的 key 就是方法的参数,缓存的 value 就是方法的返回值。

@CachePut

这个注解一般加在更新方法上,当数据库中的数据更新后,缓存中的数据也要跟着更新,使用该注解,可以将方法的返回值自动更新到已经存在的 key 上。

@CacheEvict

这个注解一般加在删除方法上,当数据库中的数据删除后,相关的缓存数据也要自动清除,该注解在使用的时候也可以配置按照某种条件删除(condition 属性)或者或者配置清除所有缓存(allEntries 属性)

原理:

CacheManager===Cache 缓存组件来实际给缓存中存取数据
1)引入redis的starter,容器中保存的是 RedisCacheManager;
2)RedisCacheManager 帮我们创建 RedisCache 来作为缓存组件;RedisCache通过操作redis缓存数据的
3)默认保存数据 k-v 都是Object;利用序列化保存;如何保存为json
1.引入了redis的starter,cacheManager变为 RedisCacheManager;
2.默认创建的 RedisCacheManager 操作redis的时候使用的是 RedisTemplate<Object, Object>
3.RedisTemplate<Object, Object> 是 默认使用jdk的序列化机制
4)自定义CacheManager;

 Spring Cache :Spring Cache学习_liwenyang1992的专栏-CSDN博客 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值