前几天在spring整合Redis的时候使用了手动的方式,也就是可以手动的向redis添加缓存与清除缓存,参考:http://www.cnblogs.com/qlqwjy/p/8562703.html
今天想的将spring注解整合Redis缓存弄明白,于是通过查阅资料,先做记录如下:
大致步骤如下:
0.spring的主配置中声明注解缓存:<cache:annotation-driven cache-manager="redisCacheManager"/>
1.maven的pom.xml文件导入架包
2.配置文件添加配置
3.spring管理bean的生成,xml文件配置
4. RedisCacheConfig redis自定义的工具类,自定义redis的key生成规则
5.在你想要做缓存的地方,使用注解进行缓存
- 0.spring的主配置中声明注解缓存:<cache:annotation-driven cache-manager="redisCacheManager"/>
注意:此步骤必须做,必须声明采用的缓存管理器是自己配置的redisCacheManager,否则会报错。
<cache:annotation-driven cache-manager="redisCacheManager"/>
- 1.maven的pom.xml文件导入架包
注意:
引入jackson是为了手动添加缓存
<!-- jedis依赖 --> <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.8.4.RELEASE</version> </dependency> <!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.1.0</version> </dependency>
- 2.配置文件添加配置redis.properties
#访问地址
redis.host=127.0.0.1
#访问端口
redis.port=6379
#注意,如果没有password,此处不设置值,但这一项要保留
redis.password=
#最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。
redis.maxIdle=300
#连接池的最大数据库连接数。设为0表示无限制
redis.maxActive=600
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
redis.maxWait=1000
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true
- 3.spring管理bean的生成,xml文件配置 applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd "> <!-- 连接池基本参数配置,类似数据库连接池 --> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true" /> <!-- redis连接池 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${redis.maxActive}" /> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- 连接池配置,类似数据库连接池 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"></property> <property name="port" value="${redis.port}"></property> <!-- <property name="password" value="${redis.pass}"></property> --> <property name="poolConfig" ref="poolConfig"></property> </bean> <!--redis操作模版,使用该对象可以操作redis --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="jedisConnectionFactory" /> <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! --> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/> </property> <!--开启事务 --> <property name="enableTransactionSupport" value="true"></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="cn.qlq.util.RedisCacheConfig"> <constructor-arg ref="jedisConnectionFactory"/> <constructor-arg ref="redisTemplate"/> <constructor-arg ref="redisCacheManager"/> </bean> <!-- 下面这个是整合Mybatis的二级缓存使用的 --> <bean id="redisCacheTransfer" class="cn.qlq.jedis.RedisCacheTransfer"> <property name="jedisConnectionFactory" ref="jedisConnectionFactory" /> </bean> </beans>
JedisPoolConfig jedis连接池配置对象
JedisConnectionFactory jedis连接工厂,生成连接对象
RedisTemplate RedisTemplate 对 RedisConnection 进行了封装。提供连接管理,序列化等功能,它对 Redis 的交互进行了更高层次的抽象,极大的方便和简化了 Redis 的操作
RedisCacheManager 做为 redis 统一的调度和管理者
RedisCacheConfig RedisCacheConfig extends org.springframework.cache.annotation.CachingConfigurerSupport,自定义redis的key生成规则,如果不在注解参数中注明key=“”的话,就采用这个类中的key生成规则生成key
- 4. RedisCacheConfig redis自定义的工具类,自定义redis的key生成规则
采用Java配置的方式注入到spring
package cn.qlq.util; import java.lang.reflect.Method; 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.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @EnableCaching @Configuration public class RedisCacheConfig extends CachingConfigurerSupport { private volatile JedisConnectionFactory jedisConnectionFactory; private volatile RedisTemplate<String, String> redisTemplate; private volatile RedisCacheManager redisCacheManager; public RedisCacheConfig() { super(); } /** * 带参数的构造方法 初始化所有的成员变量 * * @param jedisConnectionFactory * @param redisTemplate * @param redisCacheManager */ public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate<String, String> redisTemplate, RedisCacheManager redisCacheManager) { this.jedisConnectionFactory = jedisConnectionFactory; this.redisTemplate = redisTemplate; this.redisCacheManager = redisCacheManager; } public JedisConnectionFactory getJedisConnecionFactory() { return jedisConnectionFactory; } public RedisTemplate<String, String> getRedisTemplate() { return redisTemplate; } public RedisCacheManager getRedisCacheManager() { return redisCacheManager; } @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object param : params) { sb.append(param.toString()); } return sb.toString(); } }; } }
上面的自定义的key规则是类 本类名+方法名+参数名(中间没有逗号区分),例如:
Array2List.TestClassAndMethodAndParamfun1{2=ssssssssssssssssss, 1=ssssssssssssssssss}
所以我们可以对上面的自定义规则进行改造,将方法名和参数名进行隔开之后进行区分:(修改过的key的生成规则)
public Object generate(Object o, Method method, Object... params) { //规定 本类名+方法名+参数名 为key StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append("-"); sb.append(method.getName()); sb.append("-"); for (Object param : params) { sb.append(param.toString()); } return sb.toString(); }
结果:
Array2List.TestClassAndMethodAndParam-fun1-{2=ssssssssssssssssss, 1=ssssssssssssssssss}
- 5.在你想要做缓存的地方,使用注解进行缓存
先介绍几个注解
1》@CacheConfig 配置在类上,cacheNames即定义了本类中所有用到缓存的地方,都去找这个库。只要使用了这个注解,在方法上@Cacheable @CachePut @CacheEvict就可以不用写value去找具体库名了。【一般不怎么用】
2》@Cacheable 配置在方法或类上,作用:本方法执行后,先去缓存看有没有数据,如果没有,从数据库中查找出来,给缓存中存一份,返回结果,下次本方法执行,在缓存未过期情况下,先在缓存中查找,有的话直接返回,没有的话从数据库查找
3》@CachePut 类似于更新操作,即每次不管缓存中有没有结果,都从数据库查找结果,并将结果更新到缓存,并返回结果
4》@CacheEvict 用来清除用在本方法或者类上的缓存数据(用在哪里清除哪里)
例子:
最直观的表现:首次登录,会有一条数据库的查询语句在控制台。
退出再登录,不会执行数据库的查询,直接从数据库中取出缓存,登录成功。
说明:
①使用了@Cacheable(value="myUser"),即表示缓存中有,直接从缓存取出,没有的话先从数据库中查出,然后再插入
②如果未在类上使用@CacheConfig注解规定数据要缓存到哪个库中,就必须给value一个值,规定数据最后缓存到哪个redis库中
③因为redis缓存数据实际就是键值对的形式存储,因此必须给定key-value的key,这里没有给key参数赋值,所以key的生成规则按照上面工具类中规定的key生成的
④key-value的value就是本方法的返回值,如果要缓存登录用户信息,本方法需要进行修改,返回user对象就可以缓存到key-value的value中
例如:
Action:
package cn.qlq.Action; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.Validate; import org.apache.struts2.ServletActionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.github.pagehelper.PageHelper; import com.opensymphony.xwork2.ActionSupport; import cn.qlq.bean.User; import cn.qlq.service.UserService; @Controller @Scope("prototype") @SuppressWarnings("all") public class UserAction extends ActionSupport { private Map<String, Object> response; @Autowired private UserService userService; private int id; private String name; /** * 测试清除注解保存的缓存的同时手动添加单个缓存 * * @return */ public String add() { try { userService.addUser(id, name); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return "add"; } /** * 测试查询单个的时候注解添加单个缓存 * * @return * @throws Exception */ public String find() throws Exception { User user = userService.findUserById(id); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("user", user); return "find"; } /** * 测试删除单个的时候注解删除单个缓存 * @return */ public String delete() { try { userService.deleteById(id); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return "delete"; } /** * 测试注解缓存,将查出来的集合结果加入缓存 * * @return * @throws Exception */ public String findPage() throws Exception { response = new HashMap(); if (name == null) name = "111"; // 第三个参数代表排序方式 PageHelper.startPage(2, 2, "id desc"); List<User> users = userService.findUsersByPage(name); response.put("users", users); return "success"; } public Map getResponse() { return response; } public void setResponse(Map response) { this.response = response; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Service添加缓存与删除缓存:
package cn.qlq.service.impl; import java.sql.SQLException; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import cn.qlq.bean.User; import cn.qlq.mapper.UserMapper; import cn.qlq.service.UserService; import cn.qlq.util.RedisUtil; @Service public class UserServiceImpl implements UserService { @Autowired private RedisUtil redisUtil; @Autowired private UserMapper userMapper; //查询出来的时候添加单个缓存 @Cacheable(value="user",key="'user'+#id.toString()") @Override public User findUserById(int id) throws Exception { System.out.println("打印语句则没有走缓存"); return userMapper.findUserById(id); } //删除数据库的时候删除redis的缓存 @Override @CacheEvict(value="user",key="'user'+#id.toString()") public boolean deleteById(int id){ return true; } //添加缓存 @Cacheable(value="Alluser") @Override public List<User> findUsersByPage(String name) throws SQLException { System.out.println("打印语句则没有走缓存"); return userMapper.findUsersByPage(); } //清除上面的缓存,同时手动的添加一个缓存看能否实现 @CacheEvict(value="Alluser") @Override public int addUser(int id, String name) throws SQLException { redisUtil.set("mykey", "myvalue"); return userMapper.addUser(id, name); } }
解释:
findUserById()函数将单个用户存入缓存中,例如访问:http://localhost/SSM/user_find?id=1 后查看redis:
函数注解上面的user加上~value作为一个zset存入缓存,值为具体的缓存的键:
继续访问 http://localhost/SSM/user_find?id=2 http://localhost/SSM/user_find?id=3 http://localhost/SSM/user_find?id=4之后
继续访问http://localhost/SSM/user_delete?id=4 删除一个user4缓存:
总结:
此版本的redisspring-data-redis在设置缓存的时候是将value的值加上~keys存为一个zset,值就是每个具体的缓存的key。例如上面
@Cacheable(value="user",key="'user'+#id.toString()")
就是将user~keys作为一个zset,然后其值为user1.......(缓存的键)
删除缓存的时候删除指定的键,然后从指定的value加上~keys的zset中删除对应的值,完成删除一个缓存。
如果删除的时候只指定了其value,而没有指定key值,则跟据value值加上~keys作为key找到对应的zset,根据zset值获取所有的key后删除,然后删除此zset即完成删除。
最后给出这几个注解的具体参数以及使用相关配图参考。
表 1. @Cacheable 作用和配置方法
@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
表 2. @CachePut 作用和配置方法
@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数 | ||
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | 例如: @Cacheable(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
表 3. @CacheEvict 作用和配置方法
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空@CacheEvict 主要的参数 | ||
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | 例如: @CachEvict(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 | 例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”) |
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 | 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | 例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
*************************************************************************************************************************************************************************************
*************************************************************************************************************************************************************************************
spEL表达式的使用方法:http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html
关于注解实现Redis缓存的方法,只有将key设计的合理且强大,整个的缓存在项目中才能通用且高效。否则,就像我上面的简单的例子一样,真的是搞笑了。
总结:
在redis做缓存的时候最好是每个缓存的生命周期不固定,也就是分散的使缓存失效。可以设置有效期为3-9小时。具体的做法就是在Java中产生一个3-9小时的随机数。
注意:
在IDEA中整合的时候发现用注解配置Bean报错,因此将上面的KeyGenerator单独抽成类,注入到Spring中并在cache标签中指明key生成器:
KeyGenerator.java
package cn.xm.jwxt.utils; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * @Author: qlq * @Description * @Date: 22:49 2018/3/25 */ public class KeyGenerator implements org.springframework.cache.interceptor.KeyGenerator { @Override public Object generate(Object o, Method method, Object... params) { //规定 本类名+方法名+参数名 为key StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object param : params) { sb.append(param.toString()); } return sb.toString(); } }
key的生成规则也可以修改为:
public Object generate(Object o, Method method, Object... params) { //规定 本类名+方法名+参数名 为key StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append("-"); sb.append(method.getName()); sb.append("-"); for (Object param : params) { sb.append(param.toString()); } return sb.toString(); }
RedisCacheConfig.java
package cn.xm.jwxt.utils; import java.lang.reflect.Method; 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.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; /** * 注解redis缓存集成spring需要使用的配置类 java配置的方式注入bean到spring * @author liqiang * */ public class RedisCacheConfig extends CachingConfigurerSupport { private volatile JedisConnectionFactory jedisConnectionFactory; private volatile RedisTemplate<String, String> redisTemplate; private volatile RedisCacheManager redisCacheManager; public RedisCacheConfig() { super(); } /** * 带参数的构造方法 初始化所有的成员变量 * * @param jedisConnectionFactory * @param redisTemplate * @param redisCacheManager */ public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate<String, String> redisTemplate, RedisCacheManager redisCacheManager) { this.jedisConnectionFactory = jedisConnectionFactory; this.redisTemplate = redisTemplate; this.redisCacheManager = redisCacheManager; } public JedisConnectionFactory getJedisConnecionFactory() { return jedisConnectionFactory; } public RedisTemplate<String, String> getRedisTemplate() { return redisTemplate; } public RedisCacheManager getRedisCacheManager() { return redisCacheManager; } }
applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd "> <cache:annotation-driven cache-manager="redisCacheManager" key-generator="keyGenerator"/> <!-- 连接池基本参数配置,类似数据库连接池 --> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true" /> <!-- redis连接池 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${redis.maxActive}" /> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- 连接池配置,类似数据库连接池 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"></property> <property name="port" value="${redis.port}"></property> <!-- <property name="password" value="${redis.pass}"></property> --> <property name="poolConfig" ref="poolConfig"></property> </bean> <!--redis操作模版,使用该对象可以操作redis --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="jedisConnectionFactory" /> <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! --> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/> </property> <!--开启事务 --> <property name="enableTransactionSupport" value="true"></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="cn.xm.jwxt.utils.RedisCacheConfig"> <constructor-arg ref="jedisConnectionFactory"/> <constructor-arg ref="redisTemplate"/> <constructor-arg ref="redisCacheManager"/> </bean> <!----> <bean id="keyGenerator" class="cn.xm.jwxt.utils.KeyGenerator"></bean> <!-- 下面这个是整合Mybatis的二级缓存使用的 --> <!-- <bean id="redisCacheTransfer" class="cn.qlq.jedis.RedisCacheTransfer"> <property name="jedisConnectionFactory" ref="jedisConnectionFactory" /> </bean>--> </beans>
参考:https://www.cnblogs.com/sxdcgaq8080/p/7228163.html
手动redis注解集成spring:http://www.cnblogs.com/qlqwjy/p/8562703.html
关于缓存注解的更详细的使用方法参考:http://www.cnblogs.com/qlqwjy/p/8559119.html