文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

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”。

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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯