spring cache使用redis做缓存

程序员

在这里就不做spring框架详细描述,只对用的作解释,有什么问题欢迎来信。

1.pom添加

这里增加spring-data-redisjedis 必须要jar包。

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.3.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.5.2</version>
        </dependency>

2.spring 配置

在spring的配置文件applicationContext.xml里加入下面的redis的配置信息。

<!-- redis配置 -->
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
            <property name="maxIdle" value="300" />  
            <property name="maxTotal" value="600" />  
            <property name="maxWaitMillis" value="1000" />  
            <property name="testOnBorrow" value="true" />  
        </bean>

        <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"  
            p:host-name="192.168.31.4" p:port="6379" p:password=""  p:pool-config-ref="poolConfig"/>

        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
            <property name="connectionFactory" ref="connectionFactory" />
        </bean>    

这里配置redis的一些信息,当然也可以用配置文件来配置

redis.host=192.168.31.4 
redis.port=6379  
redis.pass= 
redis.maxIdle=50  
redis.maxActive=50  
redis.maxWait=50  
redis.testOnBorrow=true  
redis.timeout=1000  

接下来在配置文件中配置要缓存的对象值,如下:

<cache:annotation-driven/>
    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="com.hejia.alauda.redis.SystemRedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="default" />
                    <property name="timeout" value="600" /><!-- 10分钟后过期 -->
                </bean>
                <bean class="com.hejia.alauda.redis.SystemRedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="orderServiceImpl.selectInterests" />
                    <property name="timeout" value="600" />
                </bean>
                <bean class="com.hejia.alauda.redis.SystemRedisCache">
                    <property name="redisTemplate" ref="redisTemplate" />
                    <property name="name" value="orderServiceImpl.selectInterestsList" />
                    <property name="timeout" value="600" />
                </bean>
            </set>
        </property>
    </bean>

这里面配置的orderServiceImpl.selectInterestsorderServiceImpl.selectInterestsList 分别是redis缓存的名称,下面代码会说到

3.redis缓存配置类

这里增加配置文件中实现的SystemRedisCache ,这里主要对redis的业务的操作方法。


import com.hejia.alauda.utils.SerializableUtil;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

/**
 * info:redis缓存配置类
 * Created by shang on 2016/11/9.
 */
public class SystemRedisCache implements Cache {

    /**
     * Redis
     */
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 缓存名称
     */
    private String name;

    /**
     * 超时时间
     */
    private long timeout;

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#getName()
     */
    @Override
    public String getName() {
        return this.name;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#getNativeCache()
     */
    @Override
    public Object getNativeCache() {
        // TODO Auto-generated method stub
        return this.redisTemplate;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#get(java.lang.Object)
     */
    @Override
    public ValueWrapper get(Object key) {
        if (StringUtils.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>() {
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    byte[] key = finalKey.getBytes();
                    byte[] value = connection.get(key);
                    if (value == null) {
                        return null;
                    }
                    return SerializableUtil.unserialize(value);
                }
            });
            return (object != null ? new SimpleValueWrapper(object) : null);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#get(java.lang.Object, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object key, Class<T> type) {
        if (StringUtils.isEmpty(key) || null == type) {
            return null;
        } else {
            final String finalKey;
            final Class<T> finalType = type;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            final Object object = redisTemplate.execute(new RedisCallback<Object>() {
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    byte[] key = finalKey.getBytes();
                    byte[] value = connection.get(key);
                    if (value == null) {
                        return null;
                    }
                    return SerializableUtil.unserialize(value);
                }
            });
            if (finalType != null && finalType.isInstance(object) && null != object) {
                return (T) object;
            } else {
                return null;
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public void put(final Object key, final 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) {
                        connection.set(finalKey.getBytes(), SerializableUtil.serialize(finalValue));
                        // 设置超时间
                        connection.expire(finalKey.getBytes(), timeout);
                        return true;
                    }
                });
            }
        }
    }

    /*
     * 根据Key 删除缓存
     */
    @Override
    public void evict(Object key) {
        if (null != key) {
            final String finalKey;
            if (key instanceof String) {
                finalKey = (String) key;
            } else {
                finalKey = key.toString();
            }
            if (!StringUtils.isEmpty(finalKey)) {
                redisTemplate.execute(new RedisCallback<Long>() {
                    public Long doInRedis(RedisConnection connection) throws DataAccessException {
                        return connection.del(finalKey.getBytes());
                    }
                });
            }
        }
    }

    /*
     * 清楚系统缓存
     */
    @Override
    public void clear() {
        // TODO Auto-generated method stub
        // redisTemplate.execute(new RedisCallback<String>() {
        // public String doInRedis(RedisConnection connection) throws DataAccessException {
        // connection.flushDb();
        // return "ok";
        // }
        // });
    }

    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 long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }
}

这里附带一个工具类的代码,主要对开发中object和list对象的序列化和反序列化。
因为redis不知object和泛型,所有在将对象存入redis时,需要将缓存的数据序列化。


import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * info:序列化工具类
 * Created by shang on 2016/11/9.
 */
public class SerializableUtil {

    /**
     * 序列化
     *
     * @param object
     * @return
     */
    public static byte[] serialize(Object object) {
        if (object == null) {
            return null;
        }
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        byte[] bytes = null;
        try {
            // 序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            bytes = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(oos);
            close(baos);
        }
        return bytes;
    }

    /**
     * 反序列化
     *
     * @param bytes
     * @return
     */
    public static Object unserialize(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(bais);
            close(ois);
        }
        return null;
    }

    /**
     * 序列化 list 集合
     *
     * @param list
     * @return
     */
    public static byte[] serializeList(List<?> list) {

        if (list==null||list.size()==0) {
            return null;
        }
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        byte[] bytes = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            for (Object obj : list) {
                oos.writeObject(obj);
            }
            bytes = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(oos);
            close(baos);
        }
        return bytes;
    }

    /**
     * 反序列化 list 集合
     */
    public static List<?> unserializeList(byte[] bytes) {
        if (bytes == null) {
            return null;
        }

        List<Object> list = new ArrayList<Object>();
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            while (bais.available() > 0) {
                Object obj = (Object) ois.readObject();
                if (obj == null) {
                    break;
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(bais);
            close(ois);
        }
        return list;
    }

    /**
     * 关闭io流对象
     *
     * @param closeable
     */
    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

4.缓存service方法

对指定的service方法做缓存,使用方法如下:

 @Cacheable(value = "orderServiceImpl.selectInterests",key = "'selectInterests_'+#params.get('user_id')+'_'+#params.get('type')+'_'+#pager.pageNumber")//增加redis缓存
    @Override
    public Pager<Interest> selectInterests(Map<String, Object> params, Pager<Interest> pager) {
        System.out.println("select selectInterests class ....");
        pager.setList(interestMapper.findInterestListV3(params, pager));
        pager.setTotalCount(interestMapper.findCountInterestListV3(params));
        return pager;
    }

    @Cacheable(value = "orderServiceImpl.selectInterestsList",key = "'selectInterestsList_'+#params.get('user_id')+'_'+#params.get('type')+'_'+#params.get('valueDate')")//增加redis缓存
    @Override
    public List<Map<String, Object>> selectInterestsList(Map<String, Object> params) {
        System.out.println("select ---selectInterestsList class");
        return interestMapper.selectInterestsListV3(params);
    }

这上面的两个方法就是spring配置文件里配置的两个缓存,我这里主要是对查询的分页做缓存。
上面的方法只是添加缓存,并10分钟后过期。这里的key是在redis所对应的标识,如果要查询出来可以使用key值来查询。

如果你需要对缓存进行修改和删除,这需要使用@CachePut@CacheEvict 。使用方法如下

Cache注解详解

- @CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = "users"):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。

- @Cacheable:配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:

    - value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
    - key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档

    - condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。
    - unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
    - keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的

    - cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
    - cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。

除了这里用到的两个注解之外,还有下面几个核心注解:

- @CachePut:配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
- @CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:

    - allEntries:非必需,默认为false。当为true时,会移除所有数据
    - beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。

5.结束

上面这些就是使用spring cache对redis做缓存的用法。如果在你的项目里需要可以试试一番,这里没有使用Ecache缓存是因为项目使用分布式部署,如果是本地缓存就不行了,所以使用redis做缓存,统一存取。

有什么问题欢迎给我来信或留言!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值