在 Spring Boot 中添加 spring-boot-starter-data-redis 依赖:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
在 application.properties 中指定 redis 服务器 IP, 端口和密码, 连接数等:
- # Redis 服务器地址
- spring.redis.host=127.0.0.1
- # Redis 服务器连接端口 使用默认端口 6379 可以省略配置
- #spring.redis.port=6379
- # Redis 服务器连接密码 (默认为空)
- #spring.redis.password=
- # 连接池最大连接数 (如果配置 <=0, 则没有限制 )
- spring.redis.jedis.pool.max-active=8
使用 StringRedisTemplate 和 RedisTemplate
StringRedisTemplate 是 Spring Boot 内置的操作 Redis 的 API 实现类, 另外还有一个 API 实现类是 RedisTemplate.StringRedisTemplate 的 API 假定所有的数据类型化都是字符类型, 即 key 和 value 都是字符串类型, 对于常见额 SpringBoot 应用系统, 使用字符串操作也已经足够了, 而且能方便的通过客户端管理工具管理. StringRedisTemplate 继承 RedisTemplate, 与 RedisTemplate 不同的是重新设置了序列化策略, 使用 StringRedisSerialier 类来序列化 key-value, 包括 List,Hash,Set 等数据结构.
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class SpringbootCacheApplicationTests {
- @Autowired
- StringRedisTemplate stringRedisTemplate; // 操作 k-v 字符串
- @Autowired
- RedisTemplate redisTemplate; //k- v 都是对象
- /**
- * redis 常见
- * String(字符串) List(列表) Set(集合) Hash(散列) ZSet(有序集合)
- */
- @Test
- public void test1() {
- stringRedisTemplate.opsForValue().append("StringKey", "字符串数值");
- String value = stringRedisTemplate.opsForValue().get("StringKey");
- System.out.println(value);
- }
- @Test
- public void test2() {
- Product product = productMapper.getProductById(4L);
- redisTemplate.opsForValue().set("produxtid4",product);
- }
- }
spring-boot-autoconfigure-2.0.4.RELEASE.jar 包中 RedisAutoConfiguration.java 已经自动声明了两个 redis 操作 bean:
RedisAutoConfiguration.java 实现代码:
因此我们只要在使用的地方注入即可:
- @Autowired
- StringRedisTemplate stringRedisTemplate; // 操作 k-v 字符串
- @Autowired
- RedisTemplate redisTemplate; //k- v 都是对象
StringRedisTemplate 提供 opsForValue 用来操作 key-value, 如上面的示例, 另外还提供了一系列 opsForHash(),opsForList(),opsForSet(),opsForZSet() 等方法用来操作不同结构类型的数据.
运行上面的测试方法, 查看 redis 客户端:
可以发现, 出现了一些无法识别的字符, 查看 RedisTemplate 源码可这个是由于默认使用了 JDK 的序列化机制, 而 StringRedisTemplate 没有出乱码是因为它修改了序列化器
StringRedisTemplate 实现:
- public class StringRedisTemplate extends RedisTemplate<String, String> {
- /**
- * Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
- * and {@link #afterPropertiesSet()} still need to be called.
- */
- public StringRedisTemplate() {
- RedisSerializer<String> stringSerializer = new StringRedisSerializer();
- setKeySerializer(stringSerializer);
- setValueSerializer(stringSerializer);
- setHashKeySerializer(stringSerializer);
- setHashValueSerializer(stringSerializer);
- }
- /**
- * Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
- *
- * @param connectionFactory connection factory for creating new connections
- */
- public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
- this();
- setConnectionFactory(connectionFactory);
- afterPropertiesSet();
- }
- protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
- return new DefaultStringRedisConnection(connection);
- }
- }
- View Code
RedisTemplate 实现, 截取默认序列化器相关源码:
- @Override
- public void afterPropertiesSet() {
- super.afterPropertiesSet();
- boolean defaultUsed = false;
- if (defaultSerializer == null) {
- // 默认序列化器使用 JdkSerializationRedisSerializer
- defaultSerializer = new JdkSerializationRedisSerializer(
- classLoader != null ? classLoader : this.getClass().getClassLoader());
- }
- if (enableDefaultSerializer) {
- if (keySerializer == null) {
- keySerializer = defaultSerializer;
- defaultUsed = true;
- }
- if (valueSerializer == null) {
- valueSerializer = defaultSerializer;
- defaultUsed = true;
- }
- if (hashKeySerializer == null) {
- hashKeySerializer = defaultSerializer;
- defaultUsed = true;
- }
- if (hashValueSerializer == null) {
- hashValueSerializer = defaultSerializer;
- defaultUsed = true;
- }
- }
- if (enableDefaultSerializer && defaultUsed) {
- Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
- }
- if (scriptExecutor == null) {
- this.scriptExecutor = new DefaultScriptExecutor<>(this);
- }
- initialized = true;
- }
- View Code
既然 RedisTemplate 的默认序列化器不是很方便在 redis 管理工具中查看, 我们可以自己定义一个 RedisTemplate 实例, 修改默认的序列化器.
实现方式如下, 定义一个配置类, 重新注入一个 RedisTemplate 操作 bean:
- @Configuration
- public class MyRedisConfig {
- @Bean(name = "redisTemplate")
- public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
- RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
- // 参照 StringRedisTemplate 内部实现指定序列化器
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- redisTemplate.setKeySerializer(keySerializer());
- redisTemplate.setHashKeySerializer(keySerializer());
- redisTemplate.setValueSerializer(valueSerializer());
- redisTemplate.setHashValueSerializer(valueSerializer());
- return redisTemplate;
- }
- private RedisSerializer<String> keySerializer() {
- return new StringRedisSerializer();
- }
- // 使用 Jackson 序列化器
- private RedisSerializer<Object> valueSerializer() {
- return new GenericJackson2JsonRedisSerializer();
- }
- }
重新运行上面的测试代码, 可以发现 redis 客户端中已经可以正常的显示 json 格式数据了.
SpringBoot 集成 redis + spring cache
Spring Cache 集成 redis 的运行原理:
Spring 缓存抽象模块通过 CacheManager 来创建, 管理实际缓存组件, 当 SpringBoot 应用程序引入 spring-boot-starter-data-redi 依赖后吗, 容器中将注册的是 CacheManager 实例 RedisCacheManager 对象, RedisCacheManager 来负责创建 RedisCache 作为缓存管理组件, 由 RedisCache 操作 redis 服务器实现缓存数据操作. 实际测试发现默认注入的 RedisCacheManager 操作缓存用的是 RedisTemplate<Object, Object>, 因此我们需要自定义 cacheManager, 替换掉默认的序列化器.
实现代码:
添加 mybatis 和 redis 依赖:
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>1.3.2</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
添加 mapper 映射:
- @Mapper
- public interface ProductMapper {
- @Select("select * from tb_product where product_id=#{id}")
- Product getProductById(Long id);
- @Update("update tb_product set product_name=#{productName},product_desc=#{productDesc} WHERE product_id=#{productId}")
- int updateProduct(Product product);
- @Delete("delete from tb_product where product_id=#{id}")
- void deleteProductById(Long id);
- @Select("select * from tb_product where product_name=#{productName}")
- Product getProductByName(String productName);
- }
- Service:
- package com.sl.cache.service;
- import com.sl.cache.entity.Product;
- import com.sl.cache.mapper.ProductMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- 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.cache.annotation.Caching;
- import org.springframework.stereotype.Service;
- @Service
- @CacheConfig(cacheNames = "product")
- public class ProductService {
- @Autowired
- private ProductMapper productMapper;
- @Cacheable(cacheNames = "product1",key = "#root.methodName+'['+#id+']'")
- //@Cacheable(cacheNames = {"product1","product2"})// 默认 key 为参数, 多个参数 SimpleKey [arg1,arg2]
- //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'")
- //@Cacheable(cacheNames = "product",keyGenerator = "myKeyGenerator")
- //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'",condition="#a0>10",unless ="#a0==11") // 或者 condition="#id>10")
- public Product getProductById(Long id){
- Product product =productMapper.getProductById(id);
- System.out.println(product);
- return product;
- }
- @CachePut(value="product",key = "#result.productId",condition = "#result!=null")
- public Product updateProduct(Product product){
- int count = productMapper.updateProduct(product);
- System.out.println("影响行数:"+count);
- if(count>0){
- return product;
- }else{
- return null;
- }
- }
- //@CacheEvict(value="product",key="#id")
- //@CacheEvict(value="product",allEntries = true) // 清楚所有缓存
- @CacheEvict(value="product",allEntries = true,beforeInvocation = true) // 清楚所有缓存
- public boolean deleteProductById(Long id) {
- productMapper.deleteProductById(id);
- return true;
- }
- // 含有 CachePut 注解, 所以执行这个方法时一定会查询数据库, 及时有 cacheable 注解
- @Caching(
- cacheable = {@Cacheable(value="product",key="#productName")},
- put = {
- @CachePut(value="product",key="#result.productId"),
- @CachePut(value="product",key="#result.productName")
- }
- )
- public Product getProductByName(String productName){
- Product product =productMapper.getProductByName(productName);
- return product;
- }
- }
- Controller:
- package com.sl.cache.controller;
- import com.sl.cache.entity.Product;
- import com.sl.cache.service.ProductService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.stereotype.Service;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- @RestController
- public class ProductController {
- @Autowired
- private ProductService productService;
- @GetMapping("/product/{id}")
- public Product getProduct(@PathVariable("id") Long id) {
- Product product = productService.getProductById(id);
- return product;
- }
- //prooduct?productid=1&productName= &
- @GetMapping("/product")
- public Product updateProduct(Product product) {
- productService.updateProduct(product);
- return product;
- }
- @GetMapping("/delproduct")
- public String delProduct(@RequestParam(value="id") Long id) {
- productService.deleteProductById(id);
- return "ok";
- }
- @GetMapping("/product/name/{productName}")
- public Product getEmpByLastName(@PathVariable("productName") String productName){
- return productService.getProductByName(productName);
- }
- }
自定义 cacheManager 实现:
- package com.sl.cache.config;
- import com.sl.cache.entity.Product;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
- import org.springframework.cache.CacheManager;
- import org.springframework.cache.config.CacheManagementConfigUtils;
- 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.cache.RedisCacheWriter;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.RedisSerializationContext;
- import org.springframework.data.redis.serializer.RedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- import java.net.UnknownHostException;
- import java.time.Duration;
- @Configuration
- public class MyRedisConfig {
- @Bean(name = "redisTemplate")
- public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
- RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- redisTemplate.setKeySerializer(keySerializer());
- redisTemplate.setHashKeySerializer(keySerializer());
- redisTemplate.setValueSerializer(valueSerializer());
- redisTemplate.setHashValueSerializer(valueSerializer());
- return redisTemplate;
- }
- @Primary
- @Bean
- public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
- // 缓存配置对象
- RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
- redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L)) // 设置缓存的默认超时时间: 30 分钟
- .disableCachingNullValues() // 如果是空值, 不缓存
- .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer())) // 设置 key 序列化器
- .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer()))); // 设置 value 序列化器
- return RedisCacheManager
- .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
- .cacheDefaults(redisCacheConfiguration).build();
- }
- private RedisSerializer<String> keySerializer() {
- return new StringRedisSerializer();
- }
- private RedisSerializer<Object> valueSerializer() {
- return new GenericJackson2JsonRedisSerializer();
- }
- }
启用缓存, 添加 mybatis Mapper 映射扫描:
- @MapperScan("com.sl.cache.mapper")
- @SpringBootApplication
- @EnableCaching
- public class SpringbootCacheApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringbootCacheApplication.class, args);
- }
- }
来源: https://www.cnblogs.com/ashleyboy/p/9595584.html