spring的Cache注解和redis的区别说明

目录
  • spring Cache注解和redis区别
    • 1.不支持TTL
    • 2.内部调用
    • 3.key的问题
  • Spring Cache注解+redis整合及遇到的坑
    • 先介绍Spring Cache注解
    • 配置Spring注解与Redis整合
      • 我们在Spring-config.xml中来配置
      • 来写RedisCache这个类,让其实现Cache接口
      • 开始使用注解,我是把其作用在倒dao层中
      • 运行结果
    • key的生成策略

spring Cache注解和redis区别

1.不支持TTL

即不能设置过期时间 expires time,SpringCache 认为这是各个Cache实现自己去完成的事情,有方案但是只能设置统一的过期时间,明显不够灵活。

2.内部调用

非 public 方法上使用注解,会导致缓存无效。内部调用方法的时候不会调用cache方法。

由于 SpringCache 是基于 Spring AOP 的动态代理实现,由于代理本身的问题,当同一个类中调用另一个方法,会导致另一个方法的缓存不能使用,这个在编码上需要注意,避免在同一个类中这样调用。如果非要这样做,可以通过再次代理调用,如 ((Category)AopContext.currentProxy()).get(category) 这样避免缓存无效。

3.key的问题

在清除缓存的时候,无法指定多个缓存块,同时清除多个缓存的key。

Spring Cache注解+redis整合及遇到的坑

背景:项目经理让我做缓存,我心想不是有redis吗?他说你把Spring Cache注解结合进来,我只好硬着头皮来做。

先介绍Spring Cache注解

从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。

Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。

根据项目情况只用到@Cacheable,这里面一共有几个常用的参数,一个是value,这一定必须指定其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

还有就是key,key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。

我们这里先来看看自定义策略,至于默认策略会在后文单独介绍自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。

这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。最后一个就是condition,有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。

其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。还有几个别的注解@CacheEvict,@CachePut,基本跟@Cacheable一样只是作用不一样换汤不换药。

当你看到这的时候我相信你,你已经对Spring缓存注解了解的很多了。下面就来配置Spring注解与Redis来整合。(本人不做配置Redis)

配置Spring注解与Redis整合

先导入jar包

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.3.2</version>
</dependency>

我们在Spring-config.xml中来配置

xmlns:cache="http://www.springframework.org/schema/cache"
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.2.xsd"
<cache:annotation-driven cache-manager="cacheManager" /> //这句话一定要加上,要不注解不管用
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <bean class="com.zswy.mkedu.utils.RedisCache">
                <property name="redisTemplate" ref="redisTemplate" />
                <!--name一定要与你注解那个方法中value一样,否则找不到-->
                <property name="name" value="courseCache" />
            </bean>
        </set>
    </property>
</bean>

来写RedisCache这个类,让其实现Cache接口

public class RedisCache implements Cache{
    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public Object getNativeCache() {
        return this.redisTemplate;
    }

    public ValueWrapper get(Object key) {
        final String keyf = (String) key;
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {

                byte[] key = keyf.getBytes();
                byte[] t = name.getBytes();
                byte[] value = connection.hGet(t, key);
                if (value == null) {
                    return null;
                }
                return toObject(value);
            }
        });
        ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
        return obj;
    }

    public <T> T get(Object o, Class<T> aClass) {
        return null;
    }

    public void put(Object key, Object value) {
        final String keyf = (String) key;
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] keyb = keyf.getBytes();
                byte[] valueb = toByteArray(valuef);
                byte[] t = name.getBytes();
                connection.hSet(t, keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });
    }

    private byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    private Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }

    public ValueWrapper putIfAbsent(Object o, Object o1) {
        return null;
    }

    public void evict(Object key) {
        final String keyf = (String) key;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.hDel(name.getBytes(), keyf.getBytes());
            }
        });
    }

    public void clear() {
        // 清楚缓存,需要根据Cache的name属性,在redis中模糊查询相关key值的集合,并全部删除
        redisTemplate.execute(new RedisCallback<String>() {
            public String doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] t = name.getBytes();
                Set<byte[]> fields = connection.hKeys(t);
                for (byte[] bs : fields) {
                    connection.hDel(t, bs);
                }
                return "ok";
            }
        });
    }
}

开始使用注解,我是把其作用在倒dao层中

注意:这有一个坑:一般我们都是三层架构,service层调dao层,我一开始把注解加到Service实现层,运行的时候Cache机制不触发。我开始大量百度,查书,最后发现根本不会触发,因为Spring把实现类装载成为Bean的时候,会用代理包装一下,所以从Spring Bean的角度看,只有接口里面的方法是可见的,其它的都隐藏了,自然课看不到实现类里面的非接口方法,最后我把注解放到dao层上

这里的key我使用的方法名,一般会使用参数的属性,value就是上文在配置文件里配置的name。这样所有的配置都配好了

运行结果

这个就是我们存的key

第二个坑就是,你的实体类一定要序列化 实现Serializable,否则就报空指针异常。这个一定要记住

key的生成策略

键的生成策略有两种,一种是默认策略,一种是自定义策略。

默认策略:

默认的key是通过KeyGenerator生成的,其默认策略如下:

1.如果方法没有参数,则使用0作为key;

2.如果只有一个参数的话则使用该参数作为key;

3.如果参数多于一个则使用所有参数的hashcode作为key;

自定义策略:

自定义策略是指我们通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用参数以及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用

    maven项目中在pom.xml中依赖2个jar包,其他的spring的jar包省略: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.springfra

  • Spring Boot 基于注解的 Redis 缓存使用详解

    看文本之前,请先确定你看过上一篇文章<Spring Boot Redis 集成配置>并保证 Redis 集成后正常可用,因为本文是基于上文继续增加的代码. 一.创建 Caching 配置类 RedisKeys.Java package com.shanhy.example.redis; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import org.springf

  • Java SpringCache+Redis缓存数据详解

    目录 前言 一.什么是SpringCache 二.项目集成Spring Cache + Redis 1.配置方式 三.使用Spring Cache 四.SpringCache原理与不足 1.读模式 2.写模式:(缓存与数据库一致) 五.总结 前言 这几天学习谷粒商城又再次的回顾了一次SpringCache,之前在学习谷粒学院的时候其实已经学习了一次了!!! 这里就对自己学过来的内容进行一次的总结和归纳!!! 一.什么是SpringCache Spring Cache 是一个非常优秀的缓存组件.自

  • 配置Spring4.0注解Cache+Redis缓存的用法

    前言: 目前公司项目在上一个技术架构的处理,已经搭建好了Redis,但redis只用在了做session的管理,然而 后台的对象缓存没有用上 1. redis 和 ehcache的区别: 简单了解了下,个人觉得 从部署上而言,redis更适合分布式部署,ehcache是在每台应用服务器上开辟一块内存做缓存,集群时还得考虑缓存的情况, redis就不需要考虑缓存了.单独部署在一台服务器中(也可以是在某一台应用服务器中) 2. 项目配置(spring mvc+maven+mybaits+redis)

  • 浅谈SpringCache与redis集成实现缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性,Redis也成为一种流行的数据缓存工具. 在传统方式下对于缓存的处理代码是非常臃肿的. 例如:我们要把一个查询函数加入缓存功能,大致需要三步. 一.在函数执行前,我们需要先检查缓存中是否存在数据,如果存在则返回缓存数据 二.如果不存在,就需要在数据库的数据查询出来. 三.最后把数据存放在缓存中,当

  • spring的Cache注解和redis的区别说明

    目录 spring Cache注解和redis区别 1.不支持TTL 2.内部调用 3.key的问题 Spring Cache注解+redis整合及遇到的坑 先介绍Spring Cache注解 配置Spring注解与Redis整合 我们在Spring-config.xml中来配置 来写RedisCache这个类,让其实现Cache接口 开始使用注解,我是把其作用在倒dao层中 运行结果 key的生成策略 spring Cache注解和redis区别 1.不支持TTL 即不能设置过期时间 expi

  • Spring基于注解整合Redis完整实例

    在<Redis之--Spring整合Redis>一文中,向大家介绍了如何将spring与Redis整合起来,但不是基于注解的形式,很多同学都希望能够通过注解的形式来简单的将Spring与Redis整合起来,这样,在使用的时候,只需要在相应的方法上加上注解,便可以使方法轻松的调用Redis的缓存.那么今天就来向大家介绍如何用基于注解的形式来整合Spring与Redis. 一.项目搭建 今天,我们不使用hibernate来操作数据库了,我们今天选择的框架是: Spring4(包括mvc.conte

  • Spring与Mybatis基于注解整合Redis的方法

    基于这段时间折腾redis遇到了各种问题,想着整理一下.本文主要介绍基于Spring+Mybatis以注解的形式整合Redis.废话少说,进入正题. 首先准备Redis,我下的是Windows版,下载后直接启动redis-server就行了,见下图: 一,先上jar包 二,创建实体类 package com.sl.user.vo; import java.io.Serializable; import com.fasterxml.jackson.databind.PropertyNamingSt

  • Spring Cache手动清理Redis缓存

    这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 注册cacheRedisTemplate 将 cache 的 RedisTemplate 注册为Bean @Bean(name = "cacheRedisTemplate") public RedisTemplate cacheRedisTemplate(@Qualifier("jedisConnectionFac

  • Spring注解@Resource和@Autowired区别对比详解

    前言 @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入. 1.共同点 两者都可以写在字段和setter方法上.两者如果都写在字段上,那么就不需要再写setter方法. 2.不同点 (1)@Autowired @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory

  • 使用注解实现Redis缓存功能

    本文实例为大家分享了使用注解实现Redis缓存功能的具体代码,供大家参考,具体内容如下 非关系型内存数据库,有持久化操作, c语言编写的key,value存储系统(区别于MySQL的二维表格的形式存储.) rdb:周期性的持久化 aof:以日志形式追加 默认rdb开启,同时开启使用aof 数据类型:string.list.set.zset.hash. bitMaps 字节形式存储.geospatial 经纬度类型... 单线程:采用多路io复用实现高并发 使用: 添加依赖 <!-- redis

  • 浅谈Spring boot cache使用和原理

    缓存要解决的问题:一个程序的瓶颈在于数据库,我们也知道内存的速度是大大快于硬盘的速度的.当我们需要重复地获取相同的数据的时候,我们一次又一次的请求数据库或者远程服务,导致大量的时间耗费在数据库查询或者远程方法调用上,导致程序性能的恶化,这便是数据缓存要解决的问题. 类似的缓存技术有:Redis.EhCache.Guava等,现在一般常用的为Redis. Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如EHCa

  • spring缓存cache的使用详解

    目录 spring缓存cache的使用 springcache配置缓存存活时间 spring缓存cache的使用 在spring配置文件中添加schema和spring对缓存注解的支持: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://w

  • Spring boot详解缓存redis实现定时过期方法

    目录 前言 添加依赖 添加配置 常规缓存 开启缓存 设置缓存空间 设置缓存 增加设置缓存过期时间 总结 后记 前言 使用redis进行缓存数据,是目前比较常用的缓存解决方案.常用的缓存形式有一下几种: 1.纯原生代码进行redis的增删改查,手工编写缓存工具类,由开发者在代码中进行调用. 优势:代码由实际使用的开发者进行维护,便于定制化的改造. 2.使用市场上已有的缓存工具,也就是大家常说的大佬的轮子 优势:方便快捷,提升开发效率 添加依赖 修改pom文件引入如下配置 <?xml version

随机推荐