java使用redis缓存可以使用jedis框架,jedis操作简单,没有什么复杂的东西需要学习,网上资料很多,随便看看就会了.
将spring与redis缓存集成,其实也是使用jedis框架,只不过spring对它进行了一层封装,并将这层封装库命名为spring-data-redis.
下面将要使用spring-data-redis与jedis的jar包,并通过spring的aop功能,将redis缓存无缝无侵入的整合进来.
1.先下载好依赖包
[html]
1. <dependency>
2. <groupId>org.springframework</groupId>
3. <artifactId>spring-core</artifactId>
4. <version>4.1.1.RELEASE</version>
5. </dependency>
6. <!-- 还有spring的其它包,这里不一一贴出-->
[html]
1. <dependency>
2. <groupId>org.springframework.data</groupId>
3. <artifactId>spring-data-redis</artifactId>
4. <version>1.4.1.RELEASE</version>
5. </dependency>
6. <dependency>
7. <groupId>redis.clients</groupId>
8. <artifactId>jedis</artifactId>
9. <version>2.6.0</version>
10. </dependency>
2.再配置spring文件
[html]
1. <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
2. <property name="minIdle" value="${redis.minIdle}" />
3. <property name="maxIdle" value="${redis.maxIdle}" />
4. <property name="maxTotal" value="${redis.maxActive}" />
5. <property name="maxWaitMillis" value="${redis.maxWait}" />
6. <property name="testOnBorrow" value="${redis.testOnBorrow}" />
7. </bean>
8.
9. <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
10. <property name="hostName" value="${redis.host}" />
11. <property name="port" value="${redis.port}" />
12. <property name="password" value="${redis.password}" />
13. <property name="usePool" value="true" />
14. <property name="poolConfig" ref="poolConfig" />
15. </bean>
16.
17. <!-- redis template definition -->
18. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
19. <property name="connectionFactory" ref="jedisConnFactory" />
20. <property name="keySerializer">
21. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
22. </property>
23. <property name="valueSerializer">
24. <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
25. </property>
26. <property name="hashKeySerializer">
27. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
28. </property>
29. <property name="hashValueSerializer">
30. <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
31. </property>
32. </bean>
3.开始编写aop代码
3.1 声明两个注解类,用于定义哪些方法将使用缓存
[java]
1. @Retention(RetentionPolicy.RUNTIME)
2. @Target({ElementType.METHOD})
3. public @interface Cacheable {
4.
5. public enum KeyMode{
6. //只有加了@CacheKey的参数,才加入key后缀中
7. //只有基本类型参数,才加入key后缀中,如:String,Integer,Long,Short,Boolean
8. //所有参数都加入key后缀
9. }
10.
11. public String key() default ""; //缓存key
12. public KeyMode keyMode() default KeyMode.DEFAULT; //key的后缀模式
13. public int expire() default 0; //缓存多少秒,默认无限期
14. }
[java]
1. @Retention(RetentionPolicy.RUNTIME)
2. @Target({ElementType.PARAMETER})
3. public @interface CacheKey {}
3.2 创建一个Aop拦截器的处理类,用于拦截加了@Cacheable的方法
[java]
1. @Aspect
2. @Component
3. public class CacheableAop {
4.
5. @Autowired private RedisTemplate redisTemplate;
6.
7. @Around("@annotation(cache)")
8. public Object cached(final ProceedingJoinPoint pjp,Cacheable cache) throws Throwable {
9.
10. String key=getCacheKey(pjp, cache);
11. ValueOperations<String, Object> valueOper=redisTemplate.opsForValue();
12. //从缓存获取数据
13. if(value!=null) return value; //如果有数据,则直接返回
14.
15. //跳过缓存,到后端查询数据
16. if(cache.expire()<=0) { //如果没有设置过期时间,则无限期缓存
17. valueOper.set(key, value);
18. else { //否则设置缓存时间
19. valueOper.set(key, value,cache.expire(),TimeUnit.SECONDS);
20. }
21. return value;
22. }
23.
24. /**
25. * 获取缓存的key值
26. * @param pjp
27. * @param cache
28. * @return
29. */
30. private String getCacheKey(ProceedingJoinPoint pjp,Cacheable cache) {
31.
32. new StringBuilder();
33. ".").append(pjp.getSignature().getName());
34. if(cache.key().length()>0) {
35. ".").append(cache.key());
36. }
37.
38. Object[] args=pjp.getArgs();
39. if(cache.keyMode()==KeyMode.DEFAULT) {
40. Annotation[][] pas=((MethodSignature)pjp.getSignature()).getMethod().getParameterAnnotations();
41. for(int i=0;i<pas.length;i++) {
42. for(Annotation an:pas[i]) {
43. if(an instanceof CacheKey) {
44. ".").append(args[i].toString());
45. break;
46. }
47. }
48. }
49. else if(cache.keyMode()==KeyMode.BASIC) {
50. for(Object arg:args) {
51. if(arg instanceof String) {
52. ".").append(arg);
53. else if(arg instanceof Integer || arg instanceof Long || arg instanceof Short) {
54. ".").append(arg.toString());
55. else if(arg instanceof Boolean) {
56. ".").append(arg.toString());
57. }
58. }
59. else if(cache.keyMode()==KeyMode.ALL) {
60. for(Object arg:args) {
61. ".").append(arg.toString());
62. }
63. }
64.
65. return buf.toString();
66. }
67. }
4.使用缓存示例
[java]
1. @Service
2. @Transactional
3. public class DemoServiceImpl implements DemoService {
4.
5. @Autowired private DemoDao demoDao;
6.
7. public List<Demo> findAll() {
8. return demoDao.findAll();
9. }
10.
11. /*
12. 对get()方法配置使用缓存,缓存有效期为3600秒,缓存的key格式为:{package_name}.DemoServiceImpl.get
13. 同时为参数配置了@CacheKey后,表示此参数的值将做为key的后缀,此例的key,最终是:{package_name}.DemoServiceImpl.get.{id}
14. 可以为多个参数配置@CacheKey,拦截器会调用参数的toString()做为key的后缀
15. 若配置多个@CacheKey参数,那么最终的key格式为:{package_name}.{class_name}.{method}.{arg1}.{arg2}.{...}
16. */
17. @Cacheable(expire=3600)
18. public Demo get(@CacheKey String id) {
19. return demoDao.get(id);
20. }
21.
22. public Demo getByName(String name) {
23. return demoDao.getByName(name);
24. }
25. }
- 若为名称相同的方法配置缓存,可以在@Cacheable中加入key属性,追加额外的key后缀
- @Cacheable还有一个KeyMode属性,用于配置哪些参数可以追加到key后缀中,
默认取值 DEFAULT:表示只有加了@CacheKey的参数才能追加到key后缀
BASIC:自动将基本类型追加到key后缀,而无需再配置@CacheKey
ALL:自动将所有参数追加到lkey后缀,而无需再配置@CacheKey
这只是一个初步整合方案,测试可行,还未在生产中使用,实际效果待验正.