Spring整合Redis使用注解模式开发

第一次写博客,好紧张~~

最近在学习Redis,需求是将之前做的一个小型商城的一些数据放入Redis缓存中,避免每次都频繁的去数据库查询,降低效率。

属于摸索阶段,只能勉强实现功能,其中可能会有些有问题的地方,希望看到这篇文章的大佬能指出纠正。


项目环境:Spring+SpringMVC+Hibernate

  1. Spring-version:
    5.0.6.RELEASE
  2. Hibernate-version:
    5.2.6.Final

我使用的都是目前最新的版本


整合Redis所需的依赖

<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>2.0.1.RELEASE</version>
</dependency>

在使用注解模式之前,我是使用编码方式完成了所需功能,下面把这种方式也说一下吧。

application-redis.xml配置文件

<!--JedisPoolConfig-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲时间-->
        <property name="maxIdle" value="300"></property>
        <!--最大连接数-->
        <property name="maxTotal" value="600"></property>
        <!--最大等待时间,毫秒-->
        <property name="maxWaitMillis" value="1000"></property>
        <!--获取链接时检查有效性,默认false-->
        <property name="testOnBorrow" value="true"></property>
    </bean>

    <!--JedisConnectionFactory-->
    <bean id="jedisConnectionFactory"
            class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis_hostName}"></property>
        <property name="port" value="${redis_port}"></property>
        <property name="timeout" value="10000"></property>
        <property name="poolConfig" ref="poolConfig"></property>
    </bean>

    <!-- 存储序列化 -->
    <bean name="stringRedisSerializer"
          class="org.springframework.data.redis.serializer.StringRedisSerializer" />

    <!-- 配置RedisTemplate -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="keySerializer" ref="stringRedisSerializer" />
        <property name="hashKeySerializer" ref="stringRedisSerializer" />
        <property name="valueSerializer" ref="stringRedisSerializer" />
        <property name="hashValueSerializer" ref="stringRedisSerializer" />
    </bean>

Spring的主配置文件就不贴了,不使用缓存注解模式的话就没有需要添加的地方,就是一个普通的Spring+Hibernate的整合文件。


 配置完成后就可以开始编写代码了,其实逻辑很简单,访问的时候先通过Key判断缓存中是否存在数据,存在就直接取出数据转换格式后返回,不存在就进入数据库查询再放入缓存,返回数据。

我以我的需求为例,我目前需要将首页的广告放入缓存,从缓存中获取,因为这些资源是公开的,对所有人可见的,不必每次去数据库查询回来,所以放在缓存最合适,提高效率。

这是我的ADServiceImpl中实现获取的方法

@Override
    public List<AD> findAllByType() {
        try {
            //去缓存中查
            String adList = redisServiceImpl.get("adList");
            //不为null
            if (!WoUtil.isEmpty(adList)) {
                List<AD> ads = JSONArray.parseArray(adList,AD.class);
                //直接返回数据
                return ads;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        //缓存中不存在,就进入数据库查询,这是我自己封装的一个BaseDao的根据条件查询的方法
        List<AD> adList = adDaoImpl.findAllBy(new HashMap<String, Object>(){
            {
                this.put("type",AD_TYPE);
            }
        });
        try {
            //从数据库查出后转换类型再放入缓存
            String jsonStr = JSONArray.toJSONString(adList);
            redisServiceImpl.set("adList",jsonStr);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return adList;
    }

我这里使用的alibaba提供的fastJson,让对象和字符串之间可以轻松转换,不用使用序列化和反序列化了。

使用try{}catch{}保证缓存操作出问题不会影响正常的查询功能。

上面这样就可以完成对redis的操作了,但当我写完第一个后问题就来了,我不止广告需要存入缓存,还有其他很多地方都需要存入,比如商品分类和商品这些,那我不是都要在他们的Service加上这些代码?而且不难看出,这些代码可以说都是重复的 。

(⊙o⊙)…我使用的idea,设置了下背景,还是很好看的吧。。。。。

这两部分不是我们实际需要的代码,所以看起来很臃肿,一点也不优雅。

如果每一个Service都这样写那也太麻烦了吧,所以我找了些注解模式的文章学习,使用注解的话,配置文件中就需要添加一些东西了。

application-redis.xml,在刚才的基础上添加内容 

<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                //操作缓存的实现类
                <bean class="com.lc.shop.woEntity.RedisCache">
                    <property name="redisTemplate" ref="redisTemplate"></property>
                    //这里的value就是使用注解时使用的,用来找到name然后进入到缓存的实现类
                    <property name="name" value="adCache"></property>
                    //这里根据我的需求直接返回的时候就是我需要的类型,所以传一个type
                    <property name="type" value="com.lc.shop.pojo.AD"></property>
                    //根据需求给缓存实现类添加属性,比如过期时间,我这里暂时不用
                    <--<property name="timeout" value="5000"></property>-->
                </bean>
                <bean class="com.lc.shop.woEntity.RedisCache">
                    <property name="redisTemplate" ref="redisTemplate"></property>
                    <property name="name" value="goodsCache"></property>
                    <property name="type" value="com.lc.shop.pojo.Goods"></property>
                </bean>
            </set>
        </property>
    </bean>

在这里我使用的是

org.springframework.cache.support.SimpleCacheManager;

网上很多都是用的

org.springframework.data.redis.cache.RedisCacheManager.

我使用另一个会一直报错,找了很久也没找到问题的原因,希望有大佬能告诉我。

 

<cache:annotation-driven cache-manager="cacheManager"></cache:annotation-driven>

这个必须在spring的主配置文件中,cache-manager的名字必须和application-redis中的cacheManager一致

 然后开始编写操作缓存的实现类

//实现Cache接口
public class RedisCache<T> implements Cache {

    //缓存的名称
    private String name;

    //返回的类型
    private Class<T> type;

    //Spring提供的操作Reids的工具
    private RedisTemplate<String,Object> redisTemplate;

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

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public Class<T> getType() {
        return type;
    }

    public void setType(Class<T> type) {
        this.type = type;
    }

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

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

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


    //通过key查询缓存
    @Override
    public ValueWrapper get(Object key) {
        if (WoUtil.isEmpty(key)) {
            return null;
        }else {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            }else {
                finalKey = key.toString();
            }
            Object object = null;
            object = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                        byte[] key = finalKey.getBytes();
                        byte[] value = connection.get(key);
                        if (value == null) {
                            return null;
                        }
                        String val = new String(value);
                    List<T> tList = JSONArray.parseArray(val, type);
                    return tList;
                }
            });
            return (object != null ? new SimpleValueWrapper(object) : null);
        }
    }

    @Override
    public <T> T get(Object key, final Class<T> type) {
        return null;
    }

    @Override
    public <T> T get(Object o, Callable<T> callable) {
        return null;
    }


    //向缓存存放数据
    @Override
    public void put(Object key , Object value) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
            return;
        } else {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            if (!StringUtils.isEmpty(finalKey)) {
                final Object finalValue = value;
                redisTemplate.execute(new RedisCallback<Boolean>() {
                    @Override
                    public Boolean doInRedis(RedisConnection connection) {
                        String jsonStr = JSONArray.toJSONString(finalValue);
                        connection.set(finalKey.getBytes(),jsonStr.getBytes());
                        return true;
                    }
                });
            }
        }
    }


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


    //通过key删除缓存的数据
    @Override
    public void evict(Object key) {
        if (WoUtil.isEmpty(key)) {
            return;
        }else {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            }else {
                finalKey = key.toString();
            }
            redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    return connection.del(finalKey.getBytes());
                }
            });
        }
    }

    @Override
    public void clear() {

    }
}

这里面最主要的就是put和get方法,我只实现了put和get和一个evict就目前够用了,方法逻辑很简单,判断key是否为空,不为空就调用对应的方法,因为我都是获取的List,所有返回的是List,根据的自己的需求编写。

下面就是如何使用了,我以商品为例

    //value对应redis配置中的value
    //key只要保证唯一就可以了,我这里用的root获取当前被调用的对象的Class
    @Override
    @Cacheable(value = "goodsCache", key = "#root.targetClass")
    public List<Goods> getList() {
        List<Goods> goodsList = goodsDaoImpl.findAll();
        return goodsList;
    }

 这样看起来代码就很优雅了,里面只有我们真正需要的代码了。

Spring提供的root对象的使用

属性名称

描述

示例

methodName

当前方法名

#root.methodName

method

当前方法

#root.method.name

target

当前被调用的对象

#root.target

targetClass

当前被调用的对象的class

#root.targetClass

args

当前方法参数组成的数组

#root.args[0]

caches

当前被调用的方法使用的Cache

#root.caches[0].name

 


第一次写博客,逻辑很乱,只会把自己做的东西给大家看看,也没怎么分析,实在是抱歉,有时间会多写写锻炼自己的。 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值