前言
Redis作为缓存还是相当不错的,一定程度上缓解了数据库的IO操作,具体不多说,具体网上查找资料。
实战
不多说,直接上代码。
第一步:所需要的依赖
<!-- redis --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.8.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
第二步:Redis配置文件及其相关文件
公用参数配置
#============================#
#==== Redis settings ====#
#============================#
#redis 服务器 IP
redis.host=127.0.0.1
#redis 服务器端口
redis.port=6379
#redis 密码
redis.pass=
#redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
redis.dbIndex=0
#redis 缓存数据过期时间单位秒
redis.expiration=3000
#控制一个 pool 最多有多少个状态为 idle 的jedis实例(连接池中空闲的连接数)
redis.maxIdle=300
redis.minIdle=5
#控制一个 pool 可分配多少个jedis实例(连接池中最大连接数)
redis.maxActive=600
#当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
redis.maxWait=1000
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true
方法一
采取手动配置Redis缓存,一定要记得开启缓存
spring-redis.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <description>redis 相关类 Spring 托管</description> <!-- 开启缓存 --> <cache:annotation-driven /> <!--载入 redis 配置文件--> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> <!-- 配置 JedisPoolConfig 实例 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}"/> <property name="minIdle" value="${redis.minIdle}"/> <property name="maxTotal" value="${redis.maxActive}"/> <property name="maxWaitMillis" value="${redis.maxWait}"/> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> </bean> <!-- 配置JedisConnectionFactory --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"/> <property name="port" value="${redis.port}"/> <property name="password" value="${redis.pass}"/> <property name="database" value="${redis.dbIndex}"/> <property name="poolConfig" ref="poolConfig"/> </bean> <!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。 StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。 就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。所以根据key去删除数据的时候就出现了删除失败的问题。 --> <!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, --> <!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; --> <!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 --> <!-- 配置RedisTemplate --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <!-- <property name="valueSerializer" ref="stringRedisSerializer" /> value值如果是对象,这不能用stringRedisSerializer,报类型转换错误--> <!-- <property name="valueSerializer"> hex(十六进制)的格式 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> --> <property name="valueSerializer"> <!-- json的格式,要注意实体属性名有没有‘_’,如user_name,有的话要加注解 ,@JsonNaming会将userName处理为user_name @JsonSerialize @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) --> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> </property> </bean> <!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <!-- 这里可以配置多个redis --> <bean class="com.only.mate.utils.RedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="default" /> </bean> <bean class="com.only.mate.utils.RedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="common" /> <!-- common名称要在类或方法的注解中使用 --> </bean> <bean class="com.only.mate.utils.RedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="user" /> <!-- common名称要在类或方法的注解中使用 --> </bean> </set> </property> </bean> <!-- 配置RedisCacheManager --> <!-- <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="redisTemplate" /> <property name="defaultExpiration" value="${redis.expiration}" /> </bean> --> <!-- 配置RedisCacheConfig --> <!-- <bean id="redisCacheConfig" class="com.only.mate.utils.RedisCacheConfig"> <constructor-arg ref="jedisConnectionFactory"/> <constructor-arg ref="redisTemplate"/> <constructor-arg ref="redisCacheManager"/> </bean> --> </beans>
RedisCache.java
package com.only.mate.utils; import java.util.concurrent.Callable; 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.data.redis.serializer.RedisSerializer; /** * 仔细需要写redis缓存的key和value的序列化操作 * 此处还可做拓展: * 1.存储的时候判断该key值是否已经存在 * 2.读取的时候判断key值是否存在 * 3.拿取数据的时候判断有没有失效 * @ClassName: RedisCache * @Description: TODO * @author OnlyMate * @date 2017年11月24日 下午3:08:55 */ @SuppressWarnings({ "unchecked"}) public class RedisCache implements Cache { private RedisTemplate<String, Object> redisTemplate; private String name; private RedisSerializer<Object> keySerializer; private RedisSerializer<Object> valueSerializer; public RedisTemplate<String, Object> getRedisTemplate() { return redisTemplate; } public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; keySerializer = (RedisSerializer<Object>) redisTemplate.getKeySerializer(); valueSerializer = (RedisSerializer<Object>) redisTemplate.getValueSerializer(); } public void setName(String name) { this.name = name; } @Override public String getName() { // TODO Auto-generated method stub return this.name; } @Override public Object getNativeCache() { // TODO Auto-generated method stub return this.redisTemplate; } @Override public ValueWrapper get(Object key) { // TODO Auto-generated method stub System.out.println("get key"); final String keyf = key.toString(); Object object = null; object = redisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = keySerializer.serialize(keyf); byte[] value = connection.get(key); if (value == null) { return null; } return valueSerializer.deserialize(value); } }); return (object != null ? new SimpleValueWrapper(object) : null); } @Override public void put(Object key, Object value) { // TODO Auto-generated method stub System.out.println("put key"); final String keyf = key.toString(); final Object valuef = value; final long liveTime = 86400; redisTemplate.execute(new RedisCallback<Long>() { public Long doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyb =keySerializer.serialize(keyf); // byte[] valueb = toByteArray(valuef); byte[] valueb = valueSerializer.serialize(valuef); connection.set(keyb, valueb); if (liveTime > 0) { connection.expire(keyb, liveTime); } return 1L; } }); } /** * 此处写死了序列化,无法用xml中定义redisTemplate的key和value对应的序列化 * Title: evict * @see org.springframework.cache.Cache#evict(java.lang.Object) */ /*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; }*/ @Override public void evict(Object key) { // TODO Auto-generated method stub System.out.println("del key"); final String keyf = key.toString(); redisTemplate.execute(new RedisCallback<Long>() { public Long doInRedis(RedisConnection connection) throws DataAccessException { return connection.del(keyf.getBytes()); } }); } @Override public void clear() { // TODO Auto-generated method stub System.out.println("clear key"); redisTemplate.execute(new RedisCallback<String>() { public String doInRedis(RedisConnection connection) throws DataAccessException { connection.flushDb(); return "ok"; } }); } @Override public <T> T get(Object key, Class<T> type) { // TODO Auto-generated method stub return null; } @Override public ValueWrapper putIfAbsent(Object key, Object value) { // TODO Auto-generated method stub return null; } @Override public <T> T get(Object key, Callable<T> valueLoader) { // TODO Auto-generated method stub return null; } }
方法二
采用注解的形式开启缓存
spring-redis.xml
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <description>redis 相关类 Spring 托管</description> <!--载入 redis 配置文件--> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> <!-- 配置 JedisPoolConfig 实例 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}"/> <property name="minIdle" value="${redis.minIdle}"/> <property name="maxTotal" value="${redis.maxActive}"/> <property name="maxWaitMillis" value="${redis.maxWait}"/> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> </bean> <!-- 配置JedisConnectionFactory --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"/> <property name="port" value="${redis.port}"/> <property name="password" value="${redis.pass}"/> <property name="database" value="${redis.dbIndex}"/> <property name="poolConfig" ref="poolConfig"/> </bean> <!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。 StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。 就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。所以根据key去删除数据的时候就出现了删除失败的问题。 --> <!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, --> <!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; --> <!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 --> <!-- 配置RedisTemplate --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <!-- <property name="valueSerializer" ref="stringRedisSerializer" /> value值如果是对象,这不能用stringRedisSerializer,报类型转换错误--> <!-- <property name="valueSerializer"> hex(十六进制)的格式 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> --> <property name="valueSerializer"> <!-- json的格式,要注意实体属性名有没有‘_’,如user_name,有的话要加注解 ,@JsonNaming会将userName处理为user_name @JsonSerialize @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) --> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> </property> </bean> <!-- 配置RedisCacheManager --> <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="redisTemplate" /> <property name="defaultExpiration" value="${redis.expiration}" /> </bean> <!-- 配置RedisCacheConfig --> <bean id="redisCacheConfig" class="com.only.mate.utils.RedisCacheConfig"> <constructor-arg ref="jedisConnectionFactory"/> <constructor-arg ref="redisTemplate"/> <constructor-arg ref="redisCacheManager"/> </bean> </beans>
RedisCacheConfig.java
package com.only.mate.utils; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration @EnableCaching public class RedisCacheConfig extends CachingConfigurerSupport { protected final static Logger log = LoggerFactory.getLogger(RedisCacheConfig.class); private volatile JedisConnectionFactory mJedisConnectionFactory; private volatile RedisTemplate<String, String> mRedisTemplate; private volatile RedisCacheManager mRedisCacheManager; public RedisCacheConfig() { super(); log.info("RedisCacheConfig()"); } public RedisCacheConfig(JedisConnectionFactory mJedisConnectionFactory, RedisTemplate<String, String> mRedisTemplate, RedisCacheManager mRedisCacheManager) { super(); this.mJedisConnectionFactory = mJedisConnectionFactory; this.mRedisTemplate = mRedisTemplate; this.mRedisCacheManager = mRedisCacheManager; log.info("RedisCacheConfig(a,b,c)"); } public JedisConnectionFactory redisConnectionFactory() { return mJedisConnectionFactory; } public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) { return mRedisTemplate; } public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) { return mRedisCacheManager; } @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); } }; } }
第三步:在Spring的配置文件中引入spring-reids.xml
<import resource="classpath:spring-redis.xml" />
第四步:在Java代码中调用
下面注释掉的代码中,是手动缓存的操作
package com.only.mate.service.impl; import java.io.Serializable; import org.apache.ibatis.annotations.CacheNamespace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.alibaba.fastjson.JSON; import com.only.mate.entity.User; import com.only.mate.repository.UserMapper; import com.only.mate.service.UserService; @Service @Transactional @CacheConfig(cacheNames="user") public class UserServiceImpl implements UserService { /*@Autowired private UserDao userDao; @Override public User findOne(String username) { return userDao.getUserByUserName(username); } @Override public void save(User user) { userDao.saveUser(user); }*/ /*@Autowired protected RedisTemplate<Serializable, Serializable> redisTemplate;*/ @Autowired private UserMapper userMapper; @Override @Cacheable(value="user", key="#username") public User findOne(String username) { User user = userMapper.getUserByUserName(username); //使用最基本的方式 /*redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.set(redisTemplate.getStringSerializer().serialize("user." + user.getUserName()), redisTemplate.getStringSerializer().serialize(JSON.toJSONString(user))); return null; } });*/ //利用了StringRdisTemplate的特性 /*redisTemplate.opsForValue().set("user." + user.getUserName(), JSON.toJSONString(user));*/ //利用了StringRdisTemplate的特性 通过绑定的方式 /*BoundValueOperations<Serializable, Serializable> bound = redisTemplate.boundValueOps("user." + user.getUserName()); bound.append(JSON.toJSONString(user)); //bound.append(JSON.toJSONString(user));//追加,和StringBuilder的append一样功能 */ return user; } @Override @CachePut(value="user", key="#user.userName") public User save(User user) { Integer userId = userMapper.insert(user); if("zhangsan".equals(user.getUserName())){ throw new RuntimeException(); } return user; } }