Spring Boot Spring Cache Redis

2018-09-14 16:15:00
转贴:
http://www.cnblogs.com/ashleyboy/p/9595584.html
62

在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等数据结构。

 1 @RunWith(SpringRunner.class) 2 @SpringBootTest 3 public class SpringbootCacheApplicationTests { 4  5     @Autowired 6     StringRedisTemplate stringRedisTemplate; //操作 k-v 字符串 7  8     @Autowired 9    RedisTemplate redisTemplate;  //k- v 都是对象10 11     /**12      * redis 常见13      * String(字符串) List(列表) Set(集合) Hash(散列) ZSet(有序集合)14      */15 16     @Test17     public void test1() {18         stringRedisTemplate.opsForValue().append("StringKey", "字符串数值");19         String value = stringRedisTemplate.opsForValue().get("StringKey");20         System.out.println(value);21     }22   @Test23   public void test2() {24 25     Product product =  productMapper.getProductById(4L);26     redisTemplate.opsForValue().set("produxtid4",product);27 28 }29 }

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实现:

 1 public class StringRedisTemplate extends RedisTemplate<String, String> { 2  3    /** 4     * Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)} 5     * and {@link #afterPropertiesSet()} still need to be called. 6     */ 7    public StringRedisTemplate() { 8       RedisSerializer<String> stringSerializer = new StringRedisSerializer(); 9       setKeySerializer(stringSerializer);10       setValueSerializer(stringSerializer);11       setHashKeySerializer(stringSerializer);12       setHashValueSerializer(stringSerializer);13    }14 15    /**16     * Constructs a new <code>StringRedisTemplate</code> instance ready to be used.17     *18     * @param connectionFactory connection factory for creating new connections19     */20    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {21       this();22       setConnectionFactory(connectionFactory);23       afterPropertiesSet();24    }25 26    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {27       return new DefaultStringRedisConnection(connection);28    }29 }

RedisTemplate实现,截取默认序列化器相关源码:

 1 @Override 2 public void afterPropertiesSet() { 3  4    super.afterPropertiesSet(); 5  6    boolean defaultUsed = false; 7  8    if (defaultSerializer == null) { 9      //默认序列化器使用JdkSerializationRedisSerializer10       defaultSerializer = new JdkSerializationRedisSerializer(11             classLoader != null ? classLoader : this.getClass().getClassLoader());12    }13 14    if (enableDefaultSerializer) {15 16       if (keySerializer == null) {17          keySerializer = defaultSerializer;18          defaultUsed = true;19       }20       if (valueSerializer == null) {21          valueSerializer = defaultSerializer;22          defaultUsed = true;23       }24       if (hashKeySerializer == null) {25          hashKeySerializer = defaultSerializer;26          defaultUsed = true;27       }28       if (hashValueSerializer == null) {29          hashValueSerializer = defaultSerializer;30          defaultUsed = true;31       }32    }33 34    if (enableDefaultSerializer && defaultUsed) {35       Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");36    }37 38    if (scriptExecutor == null) {39       this.scriptExecutor = new DefaultScriptExecutor<>(this);40    }41 42    initialized = true;43 }

既然RedisTemplate的默认序列化器不是很方便在redis管理工具中查看,我们可以自己定义一个RedisTemplate实例,修改默认的序列化器。

实现方式如下,定义一个配置类,重新注入一个RedisTemplate操作bean:

 1 @Configuration 2 public class MyRedisConfig { 3  4    @Bean(name = "redisTemplate") 5      public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ 6  7         RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); 8      //参照StringRedisTemplate内部实现指定序列化器 9         redisTemplate.setConnectionFactory(redisConnectionFactory);10         redisTemplate.setKeySerializer(keySerializer());11         redisTemplate.setHashKeySerializer(keySerializer());12         redisTemplate.setValueSerializer(valueSerializer());13         redisTemplate.setHashValueSerializer(valueSerializer());14 16         return redisTemplate;17     }18 19     private RedisSerializer<String> keySerializer() {20         return new StringRedisSerializer();21     }22   //使用Jackson序列化器23     private RedisSerializer<Object> valueSerializer() {24         return new GenericJackson2JsonRedisSerializer();25     }26 }

 重新运行上面的测试代码,可以发现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映射:

 1 @Mapper 2 public interface ProductMapper { 3     @Select("select * from tb_product where product_id=#{id}") 4     Product getProductById(Long id); 5  6     @Update("update tb_product set product_name=#{productName},product_desc=#{productDesc} WHERE product_id=#{productId}") 7     int updateProduct(Product product); 8  9     @Delete("delete from tb_product where product_id=#{id}")10     void deleteProductById(Long id);11 12     @Select("select * from tb_product where product_name=#{productName}")13     Product getProductByName(String productName);14 }

Service:

 

 1 package com.sl.cache.service; 2 import com.sl.cache.entity.Product; 3 import com.sl.cache.mapper.ProductMapper; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.cache.annotation.CacheConfig; 6 import org.springframework.cache.annotation.CacheEvict; 7 import org.springframework.cache.annotation.CachePut; 8 import org.springframework.cache.annotation.Cacheable; 9 import org.springframework.cache.annotation.Caching;10 import org.springframework.stereotype.Service;11 12 @Service13 @CacheConfig(cacheNames = "product")14 public class ProductService {15     @Autowired16     private ProductMapper productMapper;17 18     @Cacheable(cacheNames = "product1",key = "#root.methodName+'['+#id+']'")19     //@Cacheable(cacheNames = {"product1","product2"})// 默认key为参数,多个参数SimpleKey [arg1,arg2]20     //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'")21     //@Cacheable(cacheNames = "product",keyGenerator = "myKeyGenerator")22     //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'",condition="#a0>10",unless = "#a0==11") //或者condition="#id>10")23     public Product getProductById(Long id){24        Product product =productMapper.getProductById(id);25        System.out.println(product);26        return product;27     }28 29     @CachePut(value="product",key = "#result.productId",condition = "#result!=null")30     public  Product updateProduct(Product product){31         int count = productMapper.updateProduct(product);32         System.out.println("影响行数:"+count);33         if(count>0){34             return product;35         }else{36             return null;37         }38     }39 40     //@CacheEvict(value="product",key="#id")41     //@CacheEvict(value="product",allEntries = true) //清楚所有缓存42     @CacheEvict(value="product",allEntries = true,beforeInvocation = true) //清楚所有缓存43     public boolean deleteProductById(Long id) {44         productMapper.deleteProductById(id);45         return true;46     }47 48     //含有CachePut注解,所以执行这个方法时一定会查询数据库,及时有cacheable注解49     @Caching(50             cacheable = {@Cacheable(value="product",key="#productName")},51             put = {52                     @CachePut(value="product",key="#result.productId"),53                     @CachePut(value="product",key="#result.productName")54             }55     )56     public  Product getProductByName(String productName){57 58         Product product =productMapper.getProductByName(productName);59 60          return product;61     }62 }

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;

@RestControllerpublic 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实现:

 1 package com.sl.cache.config; 2 import com.sl.cache.entity.Product; 3 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 4 import org.springframework.cache.CacheManager; 5 import org.springframework.cache.config.CacheManagementConfigUtils; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Primary; 9 import org.springframework.data.redis.cache.RedisCacheConfiguration;10 import org.springframework.data.redis.cache.RedisCacheManager;11 import org.springframework.data.redis.cache.RedisCacheWriter;12 import org.springframework.data.redis.connection.RedisConnectionFactory;13 import org.springframework.data.redis.core.RedisTemplate;14 import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;15 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;16 import org.springframework.data.redis.serializer.RedisSerializationContext;17 import org.springframework.data.redis.serializer.RedisSerializer;18 import org.springframework.data.redis.serializer.StringRedisSerializer;19 20 import java.net.UnknownHostException;21 import java.time.Duration;22 23 @Configuration24 public class MyRedisConfig {25 26     @Bean(name = "redisTemplate")27     public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){28 29         RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();30 31         redisTemplate.setConnectionFactory(redisConnectionFactory);32         redisTemplate.setKeySerializer(keySerializer());33         redisTemplate.setHashKeySerializer(keySerializer());34         redisTemplate.setValueSerializer(valueSerializer());35         redisTemplate.setHashValueSerializer(valueSerializer());36         return redisTemplate;37     }38 39     @Primary40     @Bean41     public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){42         //缓存配置对象43         RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();44 45         redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L)) //设置缓存的默认超时时间:30分钟46                 .disableCachingNullValues()             //如果是空值,不缓存47                 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))         //设置key序列化器48                 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer())));  //设置value序列化器49 50         return RedisCacheManager51                 .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))52                 .cacheDefaults(redisCacheConfiguration).build();53     }54     private RedisSerializer<String> keySerializer() {55         return new StringRedisSerializer();56     }57 58     private RedisSerializer<Object> valueSerializer() {59         return new GenericJackson2JsonRedisSerializer();60     }61 }

启用缓存,添加mybatis Mapper映射扫描:

 1 @MapperScan("com.sl.cache.mapper") 2 @SpringBootApplication 3 @EnableCaching 4 public class SpringbootCacheApplication { 5  6     public static void main(String[] args) { 7         SpringApplication.run(SpringbootCacheApplication.class, args); 8  9     }10 }

 


发表评论
评论通过审核后显示。