Spring Boot(八)集成Spring Cache 和 Redis

在Spring Boot中添加spring-boot-starter-data-redis依賴:java

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

在application.properties中指定redis服務器IP、端口和密碼、鏈接數等:web

# 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等數據結構。redis

 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     @Test
17     public void test1() { 18         stringRedisTemplate.opsForValue().append("StringKey", "字符串數值"); 19         String value = stringRedisTemplate.opsForValue().get("StringKey"); 20  System.out.println(value); 21  } 22   @Test 23   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:spring

RedisAutoConfiguration.java實現代碼:數據庫

所以咱們只要在使用的地方注入便可:json

@Autowired StringRedisTemplate stringRedisTemplate; //操做 k-v 字符串
@Autowired RedisTemplate redisTemplate; //k- v 都是對象

StringRedisTemplate 提供opsForValue用來操做key-value,如上面的示例,另外還提供了一系列opsForHash()、opsForList()、opsForSet()、opsForZSet()等方法用來操做不一樣結構類型的數據。緩存

運行上面的測試方法,查看redis客戶端:服務器

 

能夠發現,出現了一些沒法識別的字符,查看RedisTemplate源碼可這個是因爲默認使用了JDK的序列化機制,而StringRedisTemplate沒有出亂碼是由於它修改了序列化器數據結構

StringRedisTemplate實現:mybatis

 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 connections 19     */
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 }
View Code

RedisTemplate實現,截取默認序列化器相關源碼:

 1 @Override  2 public void afterPropertiesSet() {  3 
 4    super.afterPropertiesSet();  5 
 6    boolean defaultUsed = false;  7 
 8    if (defaultSerializer == null) {  9      //默認序列化器使用JdkSerializationRedisSerializer
10       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 }
View Code

既然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 @Service 13 @CacheConfig(cacheNames = "product") 14 public class ProductService { 15  @Autowired 16     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; @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實現:

 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 @Configuration 24 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  @Primary 40  @Bean 41     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 RedisCacheManager 51  .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 }
相關文章
相關標籤/搜索