zoukankan      html  css  js  c++  java
  • Spring Boot 集成Spring Cache 和 Redis

    在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     @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:

    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 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 }
  • 相关阅读:
    条件概率和链式法则 conditional probability & chain rule
    如何训练一个神经网络?
    高斯贝叶斯分类器
    LDA
    机器学习中的数学-线性判别分析(LDA)
    理解矩阵
    pca数学原理(转)
    SVM入门
    Top 10 Machine Learning Algorithms For Beginners
    vfp
  • 原文地址:https://www.cnblogs.com/ashleyboy/p/9595584.html
Copyright © 2011-2022 走看看