本人转自http://hbxflihua.iteye.com/blog/2320584#bc2396403

spring目前在@Cacheable和@CacheEvict等注解上不支持缓存时效设置,只允许通过配置文件设置全局时效。这样就很不方便设定时间。比如系统参数和业务数据的时效是不一样的,这给程序开发造成很大的困扰。不得已,我重写了spring的这两个注解。以下是具体实现。

首先定义@Cacheable和@CacheEvict注解类。

  1. package com.lh.common.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. import com.rd.ifaes.common.dict.ExpireTime;
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({ElementType.METHOD})
  9. public @interface Cacheable {
  10. public String key() default ""; // 缓存key
  11. public ExpireTime expire() default ExpireTime.NONE; // 缓存时效,默认无限期
  12. }
  1. package com.lh.common.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. /**
  9. * 缓存清除
  10. * @author lh
  11. * @version 3.0
  12. * @since 2016-8-28
  13. *
  14. */
  15. @Target({ ElementType.METHOD })
  16. @Retention(RetentionPolicy.RUNTIME)
  17. @Inherited
  18. @Documented
  19. public @interface CacheEvict {
  20. String key() default "";// 缓存key
  21. }

具体的切面代码(CacheAspect.java)如下:

  1. package com.lh.common.annotation;
  2. import java.util.List;
  3. import java.util.Set;
  4. import java.util.concurrent.TimeUnit;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.core.ValueOperations;
  11. import org.springframework.stereotype.Component;
  12. import org.springframework.util.CollectionUtils;
  13. import com.rd.ifaes.common.util.ReflectionUtils;
  14. import com.rd.ifaes.common.util.StringUtils;
  15. import com.rd.ifaes.core.core.util.CacheUtils;
  16. @Aspect
  17. @Component
  18. public class CacheAspect {
  19. @SuppressWarnings("rawtypes")
  20. @Autowired
  21. private RedisTemplate redisTemplate;
  22. @Around("@annotation(cache)")
  23. public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache) throws Throwable {
  24. String key = getCacheKey(pjp, cache.key());
  25. //       //方案一:使用自定义缓存工具类操作缓存
  26. //       Object value = CacheUtils.getObj(key);// 从缓存获取数据
  27. //       if (value != null) {
  28. //       return value; // 如果有数据,则直接返回
  29. //       }
  30. //       value = pjp.proceed(); // 缓存,到后端查询数据
  31. //       if (value != null) {
  32. //       CacheUtils.set(key, value, cache.expire());
  33. //       }
  34. // 方案二:使用redisTemplate操作缓存
  35. @SuppressWarnings("unchecked")
  36. ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
  37. Object value =  valueOper.get(key); // 从缓存获取数据
  38. if (value != null) {
  39. return value; // 如果有数据,则直接返回
  40. }
  41. value = pjp.proceed(); // 缓存,到后端查询数据
  42. CacheUtils.set(key, value, cache.expire());
  43. if (cache.expire().getTime() <= 0) { // 如果没有设置过期时间,则无限期缓存
  44. valueOper.set(key, value);
  45. } else { // 否则设置缓存时间
  46. valueOper.set(key, value, cache.expire().getTime(), TimeUnit.SECONDS);
  47. }
  48. return value;
  49. }
  50. @SuppressWarnings("unchecked")
  51. @Around("@annotation(evict)")
  52. public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict) throws Throwable {
  53. Object value = pjp.proceed(); // 执行方法
  54. String key = getCacheKey(pjp, evict.key());
  55. //       //方案一:使用自定义缓存工具类操作缓存
  56. //       CacheUtils.del(key);
  57. // 方案二:使用redisTemplate操作缓存
  58. if (evict.key().equals(key)) {// 支持批量删除
  59. Set<String> keys = redisTemplate.keys(key.concat("*"));
  60. redisTemplate.delete(keys);
  61. }else{
  62. redisTemplate.delete(key);
  63. }
  64. return value;
  65. }
  66. /**
  67. * 获取缓存的key值
  68. *
  69. * @param pjp
  70. * @param key
  71. * @return
  72. */
  73. private String getCacheKey(ProceedingJoinPoint pjp, String key) {
  74. StringBuilder buf = new StringBuilder();
  75. Object[] args = pjp.getArgs();
  76. if(StringUtils.isNotBlank(key)){
  77. buf.append(key);
  78. List<String> annoParamNames = AopUtils.getAnnoParams(key);
  79. String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
  80. if(!CollectionUtils.isEmpty(annoParamNames)){
  81. for (String ap : annoParamNames) {
  82. String paramValue = "";
  83. for (int i = 0; i < methodParamNames.length; i++) {
  84. if(ap.startsWith(methodParamNames[i])){
  85. Object arg = args[i];
  86. if (ap.contains(".")) {
  87. paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf(".") + 1)));
  88. } else {
  89. paramValue = String.valueOf(arg);
  90. }
  91. }
  92. }
  93. int start = buf.indexOf("{" + ap);
  94. int end = start + ap.length() + 2;
  95. buf = buf.replace(start, end, paramValue);
  96. }
  97. }
  98. }else{
  99. buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
  100. for (Object arg : args) {
  101. buf.append(":").append(arg.toString());
  102. }
  103. }
  104. return buf.toString();
  105. }
  106. }

里面使用到AopUtils.java和ExpireTime.java两个类,具体代码如下:

  1. package com.lh.common.annotation;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.Signature;
  4. import org.aspectj.lang.reflect.MethodSignature;
  5. import org.springframework.asm.*;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.lang.reflect.Method;
  9. import java.lang.reflect.Modifier;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import java.util.regex.Matcher;
  13. import java.util.regex.Pattern;
  14. /**
  15. * 切面编程工具类
  16. * @author lh
  17. * @version 3.0
  18. * @since 2016-8-26
  19. */
  20. public class AopUtils {
  21. /**
  22. * <p>获取方法的参数名</p>
  23. *
  24. * @param m
  25. * @return
  26. */
  27. public static String[] getMethodParamNames(final Method m) {
  28. final String[] paramNames = new String[m.getParameterTypes().length];
  29. final String n = m.getDeclaringClass().getName();
  30. final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  31. String className = m.getDeclaringClass().getSimpleName();
  32. ClassReader cr = null;
  33. InputStream resourceAsStream = null;
  34. try {
  35. //          cr = new ClassReader(n);
  36. //          String filePathName = Class.forName(n).getResource("EDayHqbProcessManagerImpl.class").getPath();
  37. resourceAsStream = Class.forName(n).getResourceAsStream(className + ".class");
  38. cr = new ClassReader(resourceAsStream);
  39. //          cr = new ClassReader(ClassLoader.getSystemResourceAsStream(n + ".class"));
  40. } catch (IOException e) {
  41. //e.printStackTrace();
  42. //          Exceptions.uncheck(e);
  43. } catch (ClassNotFoundException e) {
  44. //e.printStackTrace();
  45. } finally {
  46. if (resourceAsStream != null) {
  47. try {
  48. resourceAsStream.close();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }
  54. assert cr != null;
  55. cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
  56. @Override
  57. public MethodVisitor visitMethod(final int access,
  58. final String name, final String desc,
  59. final String signature, final String[] exceptions) {
  60. final Type[] args = Type.getArgumentTypes(desc);
  61. // 方法名相同并且参数个数相同
  62. if (!name.equals(m.getName())
  63. || !sameType(args, m.getParameterTypes())) {
  64. return super.visitMethod(access, name, desc, signature,
  65. exceptions);
  66. }
  67. MethodVisitor v = cv.visitMethod(access, name, desc, signature,
  68. exceptions);
  69. return new MethodVisitor(Opcodes.ASM4, v) {
  70. @Override
  71. public void visitLocalVariable(String name, String desc,
  72. String signature, Label start, Label end, int index) {
  73. int i = index - 1;
  74. // 如果是静态方法,则第一就是参数
  75. // 如果不是静态方法,则第一个是"this",然后才是方法的参数
  76. if (Modifier.isStatic(m.getModifiers())) {
  77. i = index;
  78. }
  79. if (i >= 0 && i < paramNames.length) {
  80. paramNames[i] = name;
  81. }
  82. super.visitLocalVariable(name, desc, signature, start,
  83. end, index);
  84. }
  85. };
  86. }
  87. }, 0);
  88. return paramNames;
  89. }
  90. /**
  91. * <p>比较参数类型是否一致</p>
  92. *
  93. * @param types   asm的类型({@link Type})
  94. * @param clazzes java 类型({@link Class})
  95. * @return
  96. */
  97. private static boolean sameType(Type[] types, Class<?>[] clazzes) {
  98. // 个数不同
  99. if (types.length != clazzes.length) {
  100. return false;
  101. }
  102. for (int i = 0; i < types.length; i++) {
  103. if (!Type.getType(clazzes[i]).equals(types[i])) {
  104. return false;
  105. }
  106. }
  107. return true;
  108. }
  109. /**
  110. * 取得切面调用的方法
  111. * @param pjp
  112. * @return
  113. */
  114. public static Method getMethod(ProceedingJoinPoint pjp){
  115. Signature sig = pjp.getSignature();
  116. MethodSignature msig = null;
  117. if (!(sig instanceof MethodSignature)) {
  118. throw new IllegalArgumentException("该注解只能用于方法");
  119. }
  120. msig = (MethodSignature) sig;
  121. Object target = pjp.getTarget();
  122. Method currentMethod = null;
  123. try {
  124. currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
  125. } catch (NoSuchMethodException e) {
  126. } catch (SecurityException e) {
  127. }
  128. return currentMethod;
  129. }
  130. public static List<String> getMatcher(String regex, String source) {
  131. List<String> list = new ArrayList<String>();
  132. Pattern pattern = Pattern.compile(regex);
  133. Matcher matcher = pattern.matcher(source);
  134. while (matcher.find()) {
  135. list.add(matcher.group());
  136. }
  137. return list;
  138. }
  139. /**
  140. * 取得注解参数
  141. (?=exp) 匹配exp前面的位置
  142. (?<=exp) 匹配exp后面的位置
  143. (?!exp) 匹配后面跟的不是exp的位置
  144. (?<!exp) 匹配前面不是exp的位置
  145. * @param managers
  146. * @return
  147. */
  148. public static List<String> getAnnoParams(String source){
  149. String regex = "(?<=\\{)(.+?)(?=\\})";
  150. return getMatcher(regex, source);
  151. }
  152. }
  1. package com.lh.common.dict;
  2. /**
  3. * 失效时间枚举类
  4. * @author lh
  5. * @version 3.0
  6. * @since 2016-8-25
  7. *
  8. */
  9. public enum ExpireTime {
  10. /**
  11. * 无固定期限
  12. */
  13. NONE(0, "无固定期限")
  14. /**
  15. * 1秒钟
  16. */
  17. ,ONE_SEC(1, "1秒钟")
  18. /**
  19. * 5秒钟
  20. */
  21. ,FIVE_SEC(5, "5秒钟")
  22. /**
  23. * 10秒钟
  24. */
  25. ,TEN_SEC(10, "10秒钟")
  26. /**
  27. * 30秒钟
  28. */
  29. ,HALF_A_MIN(30, "30秒钟")
  30. /**
  31. * 1分钟
  32. */
  33. ,ONE_MIN(60, "1分钟")
  34. /**
  35. * 5分钟
  36. */
  37. ,FIVE_MIN(5 * 60, "5分钟")
  38. /**
  39. * 10分钟
  40. */
  41. ,TEN_MIN(10 * 60, "10分钟")
  42. /**
  43. * 20分钟
  44. */
  45. ,TWENTY_MIN(20 * 60, "20分钟")
  46. /**
  47. * 30分钟
  48. */
  49. ,HALF_AN_HOUR(30 * 60, "30分钟")
  50. /**
  51. * 1小时
  52. */
  53. ,ONE_HOUR(60 * 60, "1小时")
  54. /**
  55. * 1天
  56. */
  57. ,ONE_DAY(24 * 60 * 60, "1天")
  58. /**
  59. * 1个月
  60. */
  61. ,ONE_MON(30 * 24 * 60 * 60, "1个月")
  62. /**
  63. * 1年
  64. */
  65. ,ONE_YEAR(365 * 24 * 60 * 60, "1年")
  66. ;
  67. /**
  68. * 时间
  69. */
  70. private final int time;
  71. /**
  72. * 描述
  73. */
  74. private final String desc;
  75. ExpireTime(int time, String desc) {
  76. this.time = time;
  77. this.desc = desc;
  78. }
  79. /**
  80. * 获取具体时间
  81. * @return
  82. */
  83. public int getTime() {
  84. return time;
  85. }
  86. /**
  87. * 获取时间描述信息
  88. * @return
  89. */
  90. public String getDesc() {
  91. return desc;
  92. }
  93. /**
  94. * 根据时间匹配失效期限
  95. * @param time
  96. * @return
  97. */
  98. public static ExpireTime match(int time){
  99. if(NONE.getTime() == time){
  100. return NONE;
  101. }else if(ONE_SEC.getTime() ==  time){
  102. return ONE_SEC;
  103. }else if(FIVE_SEC.getTime() ==  time){
  104. return FIVE_SEC;
  105. }else if(TEN_SEC.getTime() ==  time){
  106. return TEN_SEC;
  107. }else if(HALF_A_MIN.getTime() ==  time){
  108. return HALF_A_MIN;
  109. }else if(ONE_MIN.getTime() ==  time){
  110. return ONE_MIN;
  111. }else if(FIVE_MIN.getTime() ==  time){
  112. return FIVE_MIN;
  113. }else if(TEN_MIN.getTime() ==  time){
  114. return TEN_MIN;
  115. }else if(TWENTY_MIN.getTime() == time){
  116. return TWENTY_MIN;
  117. }else if(HALF_AN_HOUR.getTime() ==  time){
  118. return HALF_AN_HOUR;
  119. }else if(ONE_HOUR.getTime() ==  time){
  120. return ONE_HOUR;
  121. }else if(ONE_DAY.getTime() ==  time){
  122. return ONE_DAY;
  123. }else if(ONE_MON.getTime() ==  time){
  124. return ONE_MON;
  125. }else if(ONE_YEAR.getTime() ==  time){
  126. return ONE_YEAR;
  127. }
  128. return HALF_AN_HOUR;
  129. }
  130. }

配置中的RdRedisCache.java 代码如下:

  1. package com.lh.common.jedis;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. import net.sf.ehcache.Element;
  8. import org.springframework.cache.Cache;
  9. import org.springframework.cache.support.SimpleValueWrapper;
  10. import org.springframework.dao.DataAccessException;
  11. import org.springframework.data.redis.connection.RedisConnection;
  12. import org.springframework.data.redis.core.RedisCallback;
  13. import org.springframework.data.redis.core.RedisTemplate;
  14. public class RdRedisCache implements Cache {
  15. private RedisTemplate<String, Object> redisTemplate;
  16. private String name;
  17. public RedisTemplate<String, Object> getRedisTemplate() {
  18. return redisTemplate;
  19. }
  20. public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
  21. this.redisTemplate = redisTemplate;
  22. }
  23. public void setName(String name) {
  24. this.name = name;
  25. }
  26. @Override
  27. public String getName() {
  28. return this.name;
  29. }
  30. @Override
  31. public Object getNativeCache() {
  32. return this.redisTemplate;
  33. }
  34. @Override
  35. public ValueWrapper get(Object key) {
  36. final String keyf = obj2Str(key);
  37. Object object = null;
  38. object = redisTemplate.execute(new RedisCallback<Object>() {
  39. public Object doInRedis(RedisConnection connection)
  40. throws DataAccessException {
  41. byte[] key = keyf.getBytes();
  42. byte[] value = connection.get(key);
  43. if (value == null) {
  44. return null;
  45. }
  46. return toObject(value);
  47. }
  48. });
  49. return (object != null ? new SimpleValueWrapper(object) : null);
  50. }
  51. @Override
  52. public void put(Object key, Object value) {
  53. final String keyf = obj2Str(key);
  54. final Object valuef = value;
  55. final long liveTime = 86400;
  56. redisTemplate.execute(new RedisCallback<Long>() {
  57. public Long doInRedis(RedisConnection connection)
  58. throws DataAccessException {
  59. byte[] keyb = keyf.getBytes();
  60. byte[] valueb = toByteArray(valuef);
  61. connection.set(keyb, valueb);
  62. if (liveTime > 0) {
  63. connection.expire(keyb, liveTime);
  64. }
  65. return 1L;
  66. }
  67. });
  68. }
  69. public String obj2Str(Object key){
  70. String keyStr = null;
  71. if(key instanceof Integer){
  72. keyStr = ((Integer)key).toString();
  73. }else if(key instanceof Long){
  74. keyStr = ((Long)key).toString();
  75. }else {
  76. keyStr = (String)key;
  77. }
  78. return keyStr;
  79. }
  80. /**
  81. * 描述 : <Object转byte[]>. <br>
  82. * <p>
  83. * <使用方法说明>
  84. * </p>
  85. *
  86. * @param obj
  87. * @return
  88. */
  89. private byte[] toByteArray(Object obj) {
  90. byte[] bytes = null;
  91. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  92. try {
  93. ObjectOutputStream oos = new ObjectOutputStream(bos);
  94. oos.writeObject(obj);
  95. oos.flush();
  96. bytes = bos.toByteArray();
  97. oos.close();
  98. bos.close();
  99. } catch (IOException ex) {
  100. ex.printStackTrace();
  101. }
  102. return bytes;
  103. }
  104. /**
  105. * 描述 : <byte[]转Object>. <br>
  106. * <p>
  107. * <使用方法说明>
  108. * </p>
  109. *
  110. * @param bytes
  111. * @return
  112. */
  113. private Object toObject(byte[] bytes) {
  114. Object obj = null;
  115. try {
  116. ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
  117. ObjectInputStream ois = new ObjectInputStream(bis);
  118. obj = ois.readObject();
  119. ois.close();
  120. bis.close();
  121. } catch (IOException ex) {
  122. ex.printStackTrace();
  123. } catch (ClassNotFoundException ex) {
  124. ex.printStackTrace();
  125. }
  126. return obj;
  127. }
  128. @Override
  129. public void evict(Object key) {
  130. final String keyf = obj2Str(key);
  131. redisTemplate.execute(new RedisCallback<Long>() {
  132. public Long doInRedis(RedisConnection connection)
  133. throws DataAccessException {
  134. return connection.del(keyf.getBytes());
  135. }
  136. });
  137. }
  138. @Override
  139. public void clear() {
  140. redisTemplate.execute(new RedisCallback<String>() {
  141. public String doInRedis(RedisConnection connection)
  142. throws DataAccessException {
  143. connection.flushDb();
  144. return "ok";
  145. }
  146. });
  147. }
  148. @Override
  149. public <T> T get(Object key, Class<T> type) {
  150. ValueWrapper wrapper = get(key);
  151. return wrapper == null ? null : (T) wrapper.get();
  152. }
  153. @Override
  154. public ValueWrapper putIfAbsent(Object key, Object value) {
  155. synchronized (key) {
  156. ValueWrapper wrapper = get(key);
  157. if (wrapper != null) {
  158. return wrapper;
  159. }
  160. put(key, value);
  161. return toWrapper(new Element(key, value));
  162. }
  163. }
  164. private ValueWrapper toWrapper(Element element) {
  165. return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
  166. }
  167. }

spring配置文件的相关配置如下:

  1. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  2. <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数  -->
  3. <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 -->
  4. <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
  5. </bean>
  6. <!-- jedisPool init -->
  7. <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
  8. <constructor-arg index="0" ref="jedisPoolConfig" />
  9. <constructor-arg index="1" value="${redis.host}" type="String" />
  10. <constructor-arg index="2" value="${redis.port}" type="int" />
  11. </bean>
  12. <!-- jedis单机配置 -->
  13. <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
  14. <property name="hostName" value="${redis.host}" />
  15. <property name="port" value="${redis.port1}" />
  16. <property name="timeout" value="${redis.timeout}" />
  17. <property name="poolConfig" ref="jedisPoolConfig" />
  18. </bean>
  19. <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
  20. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
  21. p:connectionFactory-ref="jedisConnFactory" p:keySerializer-ref="stringRedisSerializer" />
  22. <!-- spring自己的缓存管理器 -->
  23. <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
  24. <property name="caches">
  25. <set>
  26. <bean class="com.lh.common.jedis.RdRedisCache" p:redis-template-ref="redisTemplate" p:name="sysCache"/>
  27. </set>
  28. </property>
  29. </bean>
  30. <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
  31. <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" />

spring-data-redis时效设置的更多相关文章

  1. spring data redis RedisTemplate操作redis相关用法

    http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...

  2. Spring Data Redis—Pub/Sub(附Web项目源码)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  3. Spring Data Redis—Pub/Sub(附Web项目源码) (转)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  4. Spring Data Redis 详解及实战一文搞定

    SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...

  5. Spring Data Redis 让 NoSQL 快如闪电(2)

    [编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...

  6. Spring Data Redis 让 NoSQL 快如闪电 (1)

    [编者按]本文作者为 Xinyu Liu,详细介绍了 Redis 的特性,并辅之以丰富的用例.在本文的第一部分,将重点概述 Redis 的方方面面.文章系国内 ITOM 管理平台 OneAPM 编译呈 ...

  7. spring data redis使用1——连接的创建

    spring data redis集成了几个Redis客户端框架,Jedis , JRedis (Deprecated since 1.7), SRP (Deprecated since 1.7) a ...

  8. Spring Data Redis学习

    本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...

  9. Spring Data Redis 2.x 中 RedisConfiguration 类的新编写方法

    在 Spring Data Redis 1.x 的时候,我们可能会在项目中编写这样一个RedisConfig类: @Configuration @EnableCaching public class ...

  10. Spring Boot使用Spring Data Redis操作Redis(单机/集群)

    说明:Spring Boot简化了Spring Data Redis的引入,只要引入spring-boot-starter-data-redis之后会自动下载相应的Spring Data Redis和 ...

随机推荐

  1. [POJ2976] Dropping tests

    传送门:>Here< 题意:给出长度相等的数组a和b,定义他们的和为$\dfrac{a_1+a_2+...+a_n}{b_1+b_2+...+b_n}$.现在可以舍弃k对元素(一对即$a[ ...

  2. windows环境pip安装时一直报错Could not fetch URL https://pypi.org/simple/xrld/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url:

    最近项目不忙了~~有开始专研的python大业,上来想用pip安装一个第三方的库,就一直报错: Could not fetch URL https://pypi.org/simple/xrld/: T ...

  3. WIN8.1下Prolific USB-to-Serial Comm Port驱动黄色感叹号问题

    文章解决来源:http://blog.csdn.net/gsj0791/article/details/17664861 在做fpga口的uart回环测试时候,由于开发板上的是usb转uart,所以需 ...

  4. 正睿 2019 省选附加赛 Day10

    A 核心就是一个公式 \[\sum_{i = 0}^{k} S(k, i) \tbinom{x}{i} i\] S是第二类斯特林数 递推公式 \(S_2(n,k)=S_2(n−1,k−1)+kS_2( ...

  5. HNOI2019

    省选总结 day0 写了下平时不经常写的模板,像什么\(LCT\),圆方树,\(exlucas\)之类的,但是一个都没考. day1 提前十几分钟进了考场,可以提前动电脑,赶紧把\(vimrc\)打了 ...

  6. CS Academy Sliding Product Sum(组合数)

    题意 有一个长为 \(N\) 的序列 \(A = [1, 2, 3, \dots, N]\) ,求所有长度 \(\le K\) 的子串权值积的和,对于 \(M\) 取模. \(N \le 10^{18 ...

  7. 自学Python4.8-生成器(方式二:生成器表达式)

    自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...

  8. rt-thread之stm32系列BSP制作方法

    @2019-01-24 [小记] bsp制作方法: 官网下载 rt-thread 源码,将路径 bsp/stm32/libraries/templates/ 下的模板文件,Copy至路径 bsp/st ...

  9. linux防火墙,高级策略策略实例详解(实例一)

    双线服务器的控制问题: 要求:写出这个电信用户访问到双线web服务器时的IP变化过程(只写源IP,目标IP,和做SNAT还是DNAT等) 你觉得有没有问题? 实验环境: 精简一点可以使用下面的四台虚拟 ...

  10. terminate called without an active exception异常

    在gcc4.4下,采用回调机制写了一个类似std::thread的线程类. 但是使用时却发生了核心已转移的错误. main函数调用的代码大致是 int main(int argc, char *arg ...