zoukankan      html  css  js  c++  java
  • SpringBoot使用Redis做缓存结合自带注解

    配置Spring Cache

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>1.16</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.3</version>
    </dependency>
    spring:
      cache:
        type: redis
        ### 配置Redis
        # Redis数据库索引(默认为0)
        redis:
          database: 0
          # Redis服务器地址
          host: mmren.edu.redis.server
          # Redis服务器连接端口
          port: 6379
          # Redis服务器连接密码(默认为空)
          password: ENC(bIx3PYzFf0CcZ+6CdAGHruIaTgY1/CyB)
          # 配置连接池
          jedis:
            pool:
              # 连接池最大连接数(使用负值表示没有限制)
              max-active: 8
              # 连接池最大阻塞等待时间(使用负值表示没有限制)
              max-wait: -1
              # 连接池中的最大空闲连接
              max-idle: 500
              # 连接池中的最小空闲连接
              min-idle: 0
          # 连接超时时间(毫秒)
          timeout: 2000
          lettuce:
            shutdown-timeout: 0
    jasypt:
      encryptor:
        password: gerrymmrenedu # 设置根密码

    缓存配置类

    @EnableCaching
    @Configuration
    public class RedisCacheConfig extends CachingConfigurerSupport {
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            // 创建Redis序列化对象
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            // 使用Jackson的序列化对象
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    
            //解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
    
            // 配置序列化(解决乱码的问题)
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    // 7 天缓存过期
                    .entryTtl(Duration.ofDays(7))
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
    
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
    
            return cacheManager;
        }
    }

    创建实体

    public class Book implements Serializable {
        private static final long serialVersionUID = 2629983876059197650L;
        
        private String id;
        private String name; // 书名
        private Integer price; // 价格
        private Date update; // 
    
        public Book(String id, String name, Integer price, Date update) {
            super();
            this.id = id;
            this.name = name;
            this.price = price;
            this.update = update;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getPrice() {
            return price;
        }
        public void setPrice(Integer price) {
            this.price = price;
        }
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public Date getUpdate() {
            return update;
        }
        public void setUpdate(Date update) {
            this.update = update;
        }
    }
    --------------------------------
    public class BookQry {
        private String id;
        private String name; // 书名
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        
        @Override
        public String toString(){
            return JSON.toJSONString(this);
        }
    }

    数据初始化类

    public abstract class AbstractService {
    
        protected static Map<String, Book> repositoryBook = new HashMap<>();
        public AbstractService() {
            super();
        }
        
        @PostConstruct
        public void init() {
            // 1
            Book book1 = new Book("1001", "牧码人", 11, new Date());
            repositoryBook.put(book1.getId(), book1);
            // 2
            Book book2 = new Book("1002", "张三", 43, new Date());
            repositoryBook.put(book2.getId(), book2);
            // 3
            Book book3 = new Book("1003", "Gerry", 5433, new Date());
            repositoryBook.put(book3.getId(), book3);
            // 4
            Book book4 = new Book("1004", "教育", 54, new Date());
            repositoryBook.put(book4.getId(), book4);
        }
    }

    自定义缓存key生成类

    @Component
    public class MyKeyGenerator implements KeyGenerator {
    
        @Override
        public Object generate(Object target, Method method, Object... params) {
            System.out.println("自定义缓存,使用第一参数作为缓存key. params = " + Arrays.toString(params));
            // 仅仅用于测试,实际不可能这么写
            return params[0] + "0";
        }
    
    }

    Spring缓存的基本用法

    @Component
    public class BookService extends AbstractService {
        private static final Logger logger = LoggerFactory.getLogger(BookService.class);
        
        // ==================== @Cacheable ========================
        /**
         * cacheNames 设置缓存的值 
         *     key:指定缓存的key,这是指参数id值。 key可以使用spEl表达式
         * @param id
         * @return
         */
        @Cacheable(cacheNames="book1", key="#id")
        public Book queryBookCacheable(String id){
            logger.info("queryBookCacheable,id={}",id);
            return repositoryBook.get(id);
        }
        
        /**
         * 这里使用另一个缓存存储缓存
         * 
         * @param id
         * @return
         */
        @Cacheable(cacheNames="book2", key="#id")
        public Book queryBookCacheable_2(String id){
            logger.info("queryBookCacheable_2,id={}",id);
            return repositoryBook.get(id);
        }
        
        /**
         * 缓存的key也可以指定对象的成员变量
         * @param qry
         * @return
         */
        @Cacheable(cacheNames="book1", key="#qry.id")
        public Book queryBookCacheableByBookQry(BookQry qry){
            logger.info("queryBookCacheableByBookQry,qry={}",qry);
            String id = qry.getId();
            Assert.notNull(id, "id can't be null!");
            String name = qry.getName();
            Book book = null;
            if(id != null){
                book = repositoryBook.get(id);
                if(book != null && !(name != null && book.getName().equals(name))){
                    book = null;
                }
            }
            return book;
        }
        
        /**
         * 以上我们使用默认的keyGenerator,对应spring的SimpleKeyGenerator 
         *     如果你的使用很复杂,我们也可以自定义myKeyGenerator的生成key
         * 
         *  key和keyGenerator是互斥,如果同时制定会出异常
         *     The key and keyGenerator parameters are mutually exclusive and an operation specifying both will result in an exception.
         * 
         * @param id
         * @return
         */
        @Cacheable(cacheNames="book3",  keyGenerator="myKeyGenerator")
        public Book queryBookCacheableUseMyKeyGenerator(String id){
            logger.info("queryBookCacheableUseMyKeyGenerator,id={}",id);
            return repositoryBook.get(id);
        }
        
        /***
         * 如果设置sync=true,
         *     如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待
         *     如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据
         * @param id
         * @return
         */
        @Cacheable(cacheNames="book3", sync=true)
        public Book queryBookCacheableWithSync(String id) {
            logger.info("begin ... queryBookCacheableByBookQry,id={}",id);
            try {
                Thread.sleep(1000 * 2);
            } catch (InterruptedException e) {
            }
            logger.info("end ... queryBookCacheableByBookQry,id={}",id);
            return repositoryBook.get(id);
        }
        
        /**
         * 条件缓存:
         * 只有满足condition的请求才可以进行缓存,如果不满足条件,则跟方法没有@Cacheable注解的方法一样
         *     如下面只有id < 3才进行缓存
         * 
         */
        @Cacheable(cacheNames="book11", condition="T(java.lang.Integer).parseInt(#id) < 3 ")
        public Book queryBookCacheableWithCondition(String id) {
            logger.info("queryBookCacheableByBookQry,id={}",id);
            return repositoryBook.get(id);
        }
        
        /**
         * 条件缓存:
         * 对不满足unless的记录,才进行缓存
         *     "unless expressions" are evaluated after the method has been called
         *     如下面:只对不满足返回 'T(java.lang.Integer).parseInt(#result.id) <3 ' 的记录进行缓存
         * @param id
         * @return
         */
        @Cacheable(cacheNames="book22", unless = "T(java.lang.Integer).parseInt(#result.id) <3 ")
        public Book queryBookCacheableWithUnless(String id) {
            logger.info("queryBookCacheableByBookQry,id={}",id);
            return repositoryBook.get(id);
        }
        
        
        // ==================== @CacheEvict ========================
        /**
         * allEntries = true: 清空book1里的所有缓存
         */
        @CacheEvict(cacheNames="book1", allEntries=true)
        public void clearBook1All(){
            logger.info("clearAll");
        }
        /**
         * 对符合key条件的记录从缓存中book1移除
         */
        @CacheEvict(cacheNames="book1", key="#id")
        public void updateBook(String id, String name){
            logger.info("updateBook");
            Book book = repositoryBook.get(id);
            if(book != null){
                book.setName(name);
                book.setUpdate(new Date());
            }
        }
        
        // ==================== @CachePut ========================
        /**
         * 每次执行都会执行方法,无论缓存里是否有值,同时使用新的返回值的替换缓存中的值
         *     这里不同于@Cacheable:@Cacheable如果缓存没有值,从则执行方法并缓存数据,如果缓存有值,则从缓存中获取值
         * @param id
         * @return
         */
        @CachePut(cacheNames="book1", key="#id")
        public Book queryBookCachePut(String id){
            logger.info("queryBookCachePut,id={}",id);
            return repositoryBook.get(id);
        }
        
    }

    @CacheConfig配置定义

    /**
     * @CacheConfig: 类级别的注解
     *     如果我们在此注解中定义cacheNames,则此类中的所有方法上@Cacheable的cacheNames默认都是此值。当然    * @Cacheable也可以重定义cacheNames的值
     *
     */
    @Component
    @CacheConfig(cacheNames="booksAll") 
    public class BookService2 extends AbstractService {
        private static final Logger logger = LoggerFactory.getLogger(BookService2.class);
        
        /**
         * 此方法的@Cacheable没有定义cacheNames,则使用类上的注解@CacheConfig里的值 cacheNames
         * @param id
         * @return
         */
        @Cacheable(key="#id")
        public Book queryBookCacheable(String id){
            logger.info("queryBookCacheable,id={}",id);
            return repositoryBook.get(id);
        }
    
        /**
         * 此方法的@Cacheable有定义cacheNames,则使用此值覆盖类注解@CacheConfig里的值cacheNames
         * 
         * @param id
         * @return
         */
        @Cacheable(cacheNames="books_custom", key="#id")
        public Book queryBookCacheable2(String id){
            logger.info("queryBookCacheable2,id={}",id);
            return repositoryBook.get(id);
        }
    }

    编写测试用例

    public class RedisCacheTests  extends  ApplicationTests{
        @Autowired
        private BookService bookService;
    
        @Test
        public void testCache1() throws Exception {
            Book book = bookService.queryBookCacheable("1001");
            convertToString(book);
        }
    
        @Test
        public void testCache2() throws Exception {
            Book book = bookService.queryBookCacheable_2("1001");
            convertToString(book);
        }
    
        @Test
        public void testCache3() throws Exception {
            BookQry qry = new BookQry();
            qry.setId("1004");
            qry.setName("教育");
            Book book = bookService.queryBookCacheableByBookQry(qry);
            convertToString(book);
        }
    
        @Test
        public void testCache4() throws Exception {
            Book book = bookService.queryBookCacheableUseMyKeyGenerator("1003");
            convertToString(book);
        }
    
        @Test
        public void testCache5() throws Exception {
            Book book = bookService.queryBookCachePut("1003");
            convertToString(book);
        }
    
        public void convertToString(Object object) {
            String jsonString = JSON.toJSONString(object, true);
            System.out.println(jsonString);
        }
    }
  • 相关阅读:
    Java 8 ThreadLocal 源码解析
    RabbitMQ 消息中间件
    MySQL 索引与查询优化
    MySQL EXPLAIN 命令: 查看查询执行计划
    迎来送往,开启新篇章
    mockito的用法
    推荐一个计算机的科普视频
    Golang查缺补漏(一)
    2019定个小目标
    golang 中的指针
  • 原文地址:https://www.cnblogs.com/lm970585581/p/12810024.html
Copyright © 2011-2022 走看看