spring-data-redis分布式
本文转载http://hbxflihua.iteye.com/blog/2383495
1、引入spring-data-redis依赖的jar 包
- <dependency>
- <groupId>org.springframework.data</groupId>
- <artifactId>spring-data-redis</artifactId>
- <version>1.7.1.RELEASE</version>
- <exclusions>
- <exclusion>
- <artifactId>spring-tx</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- <exclusion>
- <artifactId>spring-context-support</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- <exclusion>
- <artifactId>spring-core</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- <exclusion>
- <artifactId>spring-aop</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- <exclusion>
- <artifactId>spring-context</artifactId>
- <groupId>org.springframework</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- <version>2.8.1</version>
- </dependency>
2、添加缓存注解
- package com.huatech.common.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 添加缓存
- * @author lh
- *
- */
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface Cacheable {
- /**
- * 缓存key
- * @return
- */
- public String key() default "";
- /**
- * 缓存时效,默认无限期
- * @return
- */
- public long expire() default 0L;
- }
- package com.huatech.common.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 缓存清除
- * @author lh
- * @version 3.0
- * @since 2016-8-28
- *
- */
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface CacheEvict {
- /**
- * 缓存key数组
- * @return
- */
- String[] keys() default "";
- /**
- * 操作之间的缓存时间(秒)
- * @author FangJun
- * @date 2016年9月9日
- * @return 默认0,不做限制
- */
- long interval() default 0;
- }
3、添加缓存操作工具类
- package com.huatech.common.util;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.lang.reflect.ParameterizedType;
- import java.lang.reflect.Type;
- import java.util.HashMap;
- import java.util.Map;
- import org.apache.commons.lang3.ArrayUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.lang3.Validate;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- @SuppressWarnings("rawtypes")
- public class ReflectionUtils {
- private static final String SETTER_PREFIX = "set";
- private static final String GETTER_PREFIX = "get";
- private static final String CGLIB_CLASS_SEPARATOR = "$$";
- private static Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);
- public static Object invokeGetMethod(Class<?> claszz, Object o, String name) {
- Object ret = null;
- try {
- Method method = claszz.getMethod("get" + StringUtil.firstCharUpperCase(name));
- ret = method.invoke(o);
- } catch (Exception e) {
- logger.error(e.getMessage(),e);
- }
- return ret;
- }
- /**
- * 调用Getter方法.
- * 支持多级,如:对象名.对象名.方法
- */
- public static Object invokeGetter(Object obj, String propertyName) {
- Object object = obj;
- for (String name : StringUtils.split(propertyName, ".")){
- String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
- object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
- }
- return object;
- }
- /**
- * 调用Setter方法, 仅匹配方法名。
- * 支持多级,如:对象名.对象名.方法
- */
- public static void invokeSetter(Object obj, String propertyName, Object value) {
- Object object = obj;
- String[] names = StringUtils.split(propertyName, ".");
- for (int i=0; i<names.length; i++){
- if(i<names.length-1){
- String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
- object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
- }else{
- String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
- invokeMethodByName(object, setterMethodName, new Object[] { value });
- }
- }
- }
- /**
- * 调用Setter方法(赋值)
- * @param claszz
- * @param o
- * @param name
- * @param argType
- * @param args
- * @return
- */
- public static Object invokeSetter(Class<?> claszz, Object o, String name, Class<?> argType, Object args) {
- Object ret = null;
- try {
- // 非 常量 进行反射
- if (!checkModifiers(claszz, name)) {
- Method method = claszz.getMethod("set" + StringUtil.firstCharUpperCase(name), new Class[] { argType });
- ret = method.invoke(o, new Object[] { args });
- }
- } catch (Exception e) {
- logger.error("claszz:{},name:{},argType:{},args:{}",claszz,name,argType, args);
- }
- return ret;
- }
- /**
- * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
- */
- public static Object getFieldValue(final Object obj, final String fieldName) {
- Field field = getAccessibleField(obj, fieldName);
- if (field == null) {
- throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
- }
- Object result = null;
- try {
- result = field.get(obj);
- } catch (IllegalAccessException e) {
- logger.error("不可能抛出的异常{}", e.getMessage());
- }
- return result;
- }
- /**
- * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
- */
- public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
- Field field = getAccessibleField(obj, fieldName);
- if (field == null) {
- throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
- }
- try {
- field.set(obj, value);
- } catch (IllegalAccessException e) {
- logger.error("不可能抛出的异常:{}", e.getMessage());
- }
- }
- /**
- * 直接调用对象方法, 无视private/protected修饰符.
- * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
- * 同时匹配方法名+参数类型,
- */
- public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
- final Object[] args) {
- Method method = getAccessibleMethod(obj, methodName, parameterTypes);
- if (method == null) {
- throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
- }
- try {
- return method.invoke(obj, args);
- } catch (Exception e) {
- throw convertReflectionExceptionToUnchecked(e);
- }
- }
- /**
- * 直接调用对象方法, 无视private/protected修饰符,
- * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
- * 只匹配函数名,如果有多个同名函数调用第一个。
- */
- public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
- Method method = getAccessibleMethodByName(obj, methodName);
- if (method == null) {
- throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
- }
- try {
- return method.invoke(obj, args);
- } catch (Exception e) {
- throw convertReflectionExceptionToUnchecked(e);
- }
- }
- /**
- * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
- *
- * 如向上转型到Object仍无法找到, 返回null.
- */
- public static Field getAccessibleField(final Object obj, final String fieldName) {
- Validate.notNull(obj, "object can't be null");
- Validate.notBlank(fieldName, "fieldName can't be blank");
- for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
- try {
- Field field = superClass.getDeclaredField(fieldName);
- makeAccessible(field);
- return field;
- } catch (NoSuchFieldException e) {//NOSONAR
- // Field不在当前类定义,继续向上转型
- continue;// new add
- }
- }
- return null;
- }
- /**
- * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
- * 如向上转型到Object仍无法找到, 返回null.
- * 匹配函数名+参数类型。
- *
- * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
- */
- public static Method getAccessibleMethod(final Object obj, final String methodName,
- final Class<?>... parameterTypes) {
- Validate.notNull(obj, "object can't be null");
- Validate.notBlank(methodName, "methodName can't be blank");
- for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
- try {
- Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
- makeAccessible(method);
- return method;
- } catch (NoSuchMethodException e) {
- // Method不在当前类定义,继续向上转型
- continue;// new add
- }
- }
- return null;
- }
- /**
- * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
- * 如向上转型到Object仍无法找到, 返回null.
- * 只匹配函数名。
- *
- * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
- */
- public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
- Validate.notNull(obj, "object can't be null");
- Validate.notBlank(methodName, "methodName can't be blank");
- for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
- Method[] methods = searchType.getDeclaredMethods();
- for (Method method : methods) {
- if (method.getName().equals(methodName)) {
- makeAccessible(method);
- return method;
- }
- }
- }
- return null;
- }
- /**
- * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
- */
- public static void makeAccessible(Method method) {
- if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
- && !method.isAccessible()) {
- method.setAccessible(true);
- }
- }
- /**
- * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
- */
- public static void makeAccessible(Field field) {
- if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
- .isFinal(field.getModifiers())) && !field.isAccessible()) {
- field.setAccessible(true);
- }
- }
- /**
- * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
- * 如无法找到, 返回Object.class.
- * eg.
- * public UserDao extends HibernateDao<User>
- *
- * @param clazz The class to introspect
- * @return the first generic declaration, or Object.class if cannot be determined
- */
- @SuppressWarnings("unchecked")
- public static <T> Class<T> getClassGenricType(final Class clazz) {
- return getClassGenricType(clazz, 0);
- }
- /**
- * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
- * 如无法找到, 返回Object.class.
- *
- * 如public UserDao extends HibernateDao<User,Long>
- *
- * @param clazz clazz The class to introspect
- * @param index the Index of the generic ddeclaration,start from 0.
- * @return the index generic declaration, or Object.class if cannot be determined
- */
- public static Class getClassGenricType(final Class clazz, final int index) {
- Type genType = clazz.getGenericSuperclass();
- if (!(genType instanceof ParameterizedType)) {
- logger.warn("{}'s superclass not ParameterizedType",clazz.getSimpleName());
- return Object.class;
- }
- Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
- if (index >= params.length || index < 0) {
- logger.warn("Index: {}, Size of {}'s Parameterized Type: {}",index,clazz.getSimpleName(), params.length);
- return Object.class;
- }
- if (!(params[index] instanceof Class)) {
- logger.warn(" {} not set the actual class on superclass generic parameter",clazz.getSimpleName());
- return Object.class;
- }
- return (Class) params[index];
- }
- public static Class<?> getUserClass(Object instance) {
- if(instance == null){
- throw new RuntimeException("Instance must not be null");
- }
- Class clazz = instance.getClass();
- if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
- Class<?> superClass = clazz.getSuperclass();
- if (superClass != null && !Object.class.equals(superClass)) {
- return superClass;
- }
- }
- return clazz;
- }
- /**
- * 取得类中某个Field的类型名称
- * @param clazz
- * @param fieldName
- * @return
- */
- public static String getFieldTypeName(final Class clazz, String fieldName) {
- Field field = null;
- Class tclazz = clazz;
- do {
- try {
- field = tclazz.getDeclaredField(fieldName);
- } catch (NoSuchFieldException e) {
- tclazz = clazz.getSuperclass();
- } catch (SecurityException e) {
- }
- } while (tclazz != Object.class && field == null);
- return (field == null)?null:field.getType().getSimpleName();
- }
- /**
- * 将反射时的checked exception转换为unchecked exception.
- */
- public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
- if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
- || e instanceof NoSuchMethodException) {
- return new IllegalArgumentException(e);
- } else if (e instanceof InvocationTargetException) {
- return new RuntimeException(((InvocationTargetException) e).getTargetException());
- } else if (e instanceof RuntimeException) {
- return (RuntimeException) e;
- }
- return new RuntimeException("Unexpected Checked Exception.", e);
- }
- /**
- * object 属性名称及属性值组装为String字符串。
- * 组装规则:
- * field.name1=field.value1&field.name2=field.value2 ...
- * @param object
- * @return
- */
- public static String objToString(Object object) {
- Class<?> clazz = object.getClass();
- Field[] fss = new Field[0];
- for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
- try {
- Field[] fs = clazz.getDeclaredFields();
- fss = ArrayUtils.addAll(fss, fs);
- } catch (Exception e) {
- // 这里异常不能抛出去。
- // 如果这里的异常打印或者往外抛,就不会执行clazz = clazz.getSuperclass(),
- // 最后就不会进入到父类中了
- }
- }
- StringBuffer sb = new StringBuffer(50);
- for (Field f : fss) {
- // 反射对象中String类型,且不为常量的字段
- if (String.class.equals(f.getType()) && !isConstant(f.getModifiers())) {
- String fieldName = f.getName();
- Object o = invokeGetMethod(f.getDeclaringClass(), object, fieldName);
- String value = null==o?"":o.toString();
- if (value == "") {
- continue;
- }
- sb.append(fieldName + "=" + value + "&");
- }
- }
- logger.info("请求参数:"+sb.toString());
- return sb.toString();
- }
- /**
- * 是否为常量
- * @param modifiers
- * @return 常量返回true,非常量返回false
- */
- private static boolean isConstant(int modifiers) {
- // static 和 final修饰
- if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
- return true;
- }
- return false;
- }
- /**
- * 校验参数类型
- * 目前只校验是否为 常量
- * @param claszz
- * @param name
- * @return 常量返回true,非常量返回false
- */
- private static boolean checkModifiers(Class<?> claszz, String name) {
- try {
- Field field = claszz.getField(name);
- if (isConstant(field.getModifiers())) {
- return true;
- }
- } catch (NoSuchFieldException | SecurityException e) {
- return false;
- }
- return false;
- }
- /**
- * 取得属性
- * @param clazz
- * @return
- */
- public static Map<String, Field> getClassField(Class<?> clazz) {
- Field[] declaredFields = clazz.getDeclaredFields();
- Map<String, Field> fieldMap = new HashMap<String, Field>();
- Map<String, Field> superFieldMap = new HashMap<String, Field>();
- for (Field field : declaredFields) {
- fieldMap.put(field.getName(), field);
- }
- if (clazz.getSuperclass() != null) {
- superFieldMap = getClassField(clazz.getSuperclass());
- }
- fieldMap.putAll(superFieldMap);
- return fieldMap;
- }
- }
- package com.huatech.common.util;
- import org.apache.commons.lang3.StringUtils;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.Signature;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.asm.*;
- import org.springframework.util.CollectionUtils;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * 切面编程工具类
- * @author lh
- * @version 3.0
- * @since 2016-8-26
- */
- public class AopUtils {
- private static final Logger LOGGER = LoggerFactory.getLogger(AopUtils.class);
- private static final String DESC_DOUBLE = "D";
- private static final String DESC_SHORT = "J";
- private AopUtils() { }
- /**
- * <p>获取方法的参数名</p>
- *
- * @param m
- * @return
- */
- public static String[] getMethodParamNames(final Method m) {
- final String[] paramNames = new String[m.getParameterTypes().length];
- final String n = m.getDeclaringClass().getName();
- final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- String className = m.getDeclaringClass().getSimpleName();
- ClassReader cr = null;
- InputStream resourceAsStream = null;
- try {
- resourceAsStream = Class.forName(n).getResourceAsStream(className + ".class");
- cr = new ClassReader(resourceAsStream);
- } catch (IOException | ClassNotFoundException e) {
- LOGGER.warn(e.getMessage(), e);
- } finally {
- if (resourceAsStream != null) {
- try {
- resourceAsStream.close();
- } catch (IOException e) {
- LOGGER.warn(e.getMessage(), e);
- }
- }
- }
- if (cr != null) {
- cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
- @Override
- public MethodVisitor visitMethod(final int access,
- final String name, final String desc,
- final String signature, final String[] exceptions) {
- final Type[] args = Type.getArgumentTypes(desc);
- // 方法名相同并且参数个数相同
- if (!name.equals(m.getName())
- || !sameType(args, m.getParameterTypes())) {
- return super.visitMethod(access, name, desc, signature,
- exceptions);
- }
- MethodVisitor v = cv.visitMethod(access, name, desc, signature,
- exceptions);
- return new MethodVisitor(Opcodes.ASM4, v) {
- int fixCount = 0;//步长修正计数器
- @Override
- public void visitLocalVariable(String name, String desc,
- String signature, Label start, Label end, int index) {
- int i = index - 1;
- // 如果是静态方法,则第一就是参数
- // 如果不是静态方法,则第一个是"this",然后才是方法的参数
- if (Modifier.isStatic(m.getModifiers())) {
- i = index;
- }
- if (i > fixCount) {
- i -= fixCount;
- }
- if(desc.equals(DESC_SHORT) || desc.equals(DESC_DOUBLE)){
- fixCount++;
- }
- if (i >= 0 && i < paramNames.length) {
- paramNames[i] = name;
- }
- super.visitLocalVariable(name, desc, signature, start,
- end, index);
- }
- };
- }
- }, 0);
- }
- return paramNames;
- }
- /**
- * <p>比较参数类型是否一致</p>
- *
- * @param types asm的类型({@link Type})
- * @param clazzes java 类型({@link Class})
- * @return
- */
- private static boolean sameType(Type[] types, Class<?>[] clazzes) {
- // 个数不同
- if (types.length != clazzes.length) {
- return false;
- }
- for (int i = 0; i < types.length; i++) {
- if (!Type.getType(clazzes[i]).equals(types[i])) {
- return false;
- }
- }
- return true;
- }
- /**
- * 取得切面调用的方法
- * @param pjp
- * @return
- */
- public static Method getMethod(ProceedingJoinPoint pjp){
- Signature sig = pjp.getSignature();
- if (!(sig instanceof MethodSignature)) {
- throw new IllegalArgumentException("该注解只能用于方法");
- }
- MethodSignature msig = (MethodSignature) sig;
- Object target = pjp.getTarget();
- Method currentMethod = null;
- try {
- currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
- } catch (NoSuchMethodException | SecurityException e) {
- LOGGER.warn(e.getMessage(), e);
- }
- return currentMethod;
- }
- public static List<String> getMatcher(String regex, String source) {
- List<String> list = new ArrayList<>();
- Pattern pattern = Pattern.compile(regex);
- Matcher matcher = pattern.matcher(source);
- while (matcher.find()) {
- list.add(matcher.group());
- }
- return list;
- }
- /**
- * 取得注解参数
- (?=exp) 匹配exp前面的位置
- (?<=exp) 匹配exp后面的位置
- (?!exp) 匹配后面跟的不是exp的位置
- (?<!exp) 匹配前面不是exp的位置
- * @param managers
- * @return
- */
- public static List<String> getAnnoParams(String source){
- String regex = "(?<=\\{)(.+?)(?=\\})";
- return getMatcher(regex, source);
- }
- /**
- * 获取缓存的key值
- *
- * @param pjp
- * @param key
- * @return
- */
- public static String getCacheKey(final ProceedingJoinPoint pjp, final String key) {
- StringBuilder buf = new StringBuilder();
- final Object[] args = pjp.getArgs();
- if(StringUtils.isNotBlank(key)){
- buf.append(key);
- List<String> annoParamNames = AopUtils.getAnnoParams(key);
- String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
- if(!CollectionUtils.isEmpty(annoParamNames)){
- for (String ap : annoParamNames) {
- buf = replaceParam(buf, args, methodParamNames, ap);
- }
- }
- }else{
- buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
- for (Object arg : args) {
- buf.append(":").append(arg.toString());
- }
- }
- return buf.toString();
- }
- /**
- * 替换占位参数
- * @param buf
- * @param args
- * @param methodParamNames
- * @param ap
- * @return
- */
- private static StringBuilder replaceParam(StringBuilder buf, final Object[] args, String[] methodParamNames, String ap) {
- StringBuilder builder = new StringBuilder(buf);
- String paramValue = "";
- for (int i = 0; i < methodParamNames.length; i++) {
- if(ap.startsWith(methodParamNames[i])){
- final Object arg = args[i];
- if (ap.contains(".")) {
- paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf('.') + 1)));
- } else {
- paramValue = String.valueOf(arg);
- }
- break;
- }
- }
- int start = builder.indexOf("{" + ap);
- int end = start + ap.length() + 2;
- builder =builder.replace(start, end, paramValue);
- return builder;
- }
- }
- package com.huatech.common.util;
- public class StringUtil {
- /**
- * 首字母大写
- *
- * @param s
- * @return
- */
- public static String firstCharUpperCase(String s) {
- StringBuffer sb = new StringBuffer(s.substring(0, 1).toUpperCase());
- sb.append(s.substring(1, s.length()));
- return sb.toString();
- }
- }
- package com.huatech.common.util;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
- import org.apache.commons.lang3.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.data.redis.core.BoundHashOperations;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- import com.huatech.common.support.SpringContextHolder;
- /**
- * 通用缓存工具类
- * @author lh
- * @version 3.0
- * @since 2016-6-22
- *
- */
- public class CacheUtils {
- private static final Logger LOGGER = LoggerFactory.getLogger(CacheUtils.class);
- public static RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
- public static StringRedisTemplate stringRedisTemplate = SpringContextHolder.getBean("stringRedisTemplate");
- private static String redisKeyPrefix = PropertiesUtil.getValueByKey(CacheUtils.class.getResource("/").getPath() + "config/redis.properties", "redis.keyPrefix");
- private CacheUtils() {
- }
- /**
- * 删除缓存<br>
- * 根据key精确匹配删除
- *
- * @param key
- */
- public static void del(String... key) {
- LOGGER.warn("delete cache, keys in ({})", merge(key));
- for (String k : key) {
- redisTemplate.delete(appendKeyPrefix(k));
- }
- }
- /**
- * 批量删除<br>
- * (该操作会执行模糊查询,请尽量不要使用,以免影响性能或误删)
- *
- * @param pattern
- */
- public static void batchDel(String... pattern) {
- LOGGER.warn("batchDel cache, pattern in ({})", merge(pattern));
- for (String kp : pattern) {
- redisTemplate.delete(redisTemplate.keys(appendKeyPrefix(kp) + "*"));
- }
- }
- /**
- * 取得缓存(int型)
- *
- * @param key
- * @return
- */
- public static Integer getInt(String key) {
- String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- if (StringUtils.isNotBlank(value)) {
- return Integer.valueOf(value);
- }
- return 0;
- }
- /**
- * 取得缓存(long型)
- *
- * @param key
- * @return
- */
- public static Long getLong(String key) {
- String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- if (StringUtils.isNotBlank(value)) {
- return Long.valueOf(value);
- }
- return 0l;
- }
- /**
- * 取得缓存(字符串类型)
- *
- * @param key
- * @return
- */
- public static String getStr(String key) {
- return stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- }
- /**
- * 取得缓存(字符串类型)
- *
- * @param key
- * @return
- */
- public static String getStr(String key, boolean retain) {
- String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- if (!retain) {
- stringRedisTemplate.delete(appendKeyPrefix(key));
- }
- return value;
- }
- /**
- * 获取缓存<br>
- * 注:基本数据类型(Character除外),请直接使用get(String key, Class<T> clazz)取值
- *
- * @param key
- * @return
- */
- public static Object getObj(String key) {
- return redisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- }
- /**
- * 获取缓存<br>
- * 注:java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
- *
- * @param key
- * @param retain
- * 是否保留
- * @return
- */
- public static Object getObj(String key, boolean retain) {
- Object obj = redisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- if (!retain && obj != null) {
- redisTemplate.delete(appendKeyPrefix(key));
- }
- return obj;
- }
- /**
- * 获取缓存<br>
- * 注:慎用java基本数据类型进行转换(可能会出现空值,转换报错)
- *
- * @param key
- * key
- * @param clazz
- * 类型
- * @return
- */
- @SuppressWarnings("unchecked")
- public static <T> T get(String key, Class<T> clazz) {
- key = appendKeyPrefix(key);
- if (clazz.equals(String.class)) {
- return (T) stringRedisTemplate.boundValueOps(key).get();
- } else if (clazz.equals(Integer.class) || clazz.equals(Long.class)) {
- return (T) stringRedisTemplate.boundValueOps(key).get();
- } else if (clazz.equals(Double.class) || clazz.equals(Float.class)) {
- return (T) stringRedisTemplate.boundValueOps(key).get();
- } else if (clazz.equals(Short.class) || clazz.equals(Boolean.class)) {
- return (T) stringRedisTemplate.boundValueOps(key).get();
- }
- return (T) redisTemplate.boundValueOps(key).get();
- }
- /**
- * 将value对象写入缓存
- *
- * @param key
- * @param value
- * @param seconds
- * 失效时间(秒)
- */
- public static void set(String key, Object value, long seconds) {
- if (null == key || null == value) {
- throw new RuntimeException("key or value must not null");
- }
- key = appendKeyPrefix(key);
- if (value instanceof String) {
- stringRedisTemplate.opsForValue().set(key, value.toString());
- } else if (value instanceof Integer || value instanceof Long) {
- stringRedisTemplate.opsForValue().set(key, value.toString());
- } else if (value instanceof Double || value instanceof Float) {
- stringRedisTemplate.opsForValue().set(key, value.toString());
- } else if (value instanceof Short || value instanceof Boolean) {
- stringRedisTemplate.opsForValue().set(key, value.toString());
- } else {
- redisTemplate.opsForValue().set(key, value);
- }
- if (seconds > 0) {
- redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
- }
- }
- /**
- * 更新key对象field的值
- *
- * @param key
- * 缓存key
- * @param field
- * 缓存对象field
- * @param value
- * 缓存对象field值
- */
- public static void setJsonField(String key, String field, String value) {
- JSONObject obj = JSON.parseObject(stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get());
- obj.put(field, value);
- stringRedisTemplate.opsForValue().set(appendKeyPrefix(key), obj.toJSONString());
- }
- /**
- * 递减操作
- *
- * @param key
- * @param by
- * @return
- */
- public static double decr(String key, double by) {
- return redisTemplate.opsForValue().increment(appendKeyPrefix(key), -by);
- }
- /**
- * 递增操作
- *
- * @param key
- * @param by
- * @return
- */
- public static double incr(String key, double by) {
- return redisTemplate.opsForValue().increment(appendKeyPrefix(key), by);
- }
- /**
- * 递减操作
- *
- * @param key
- * @param by
- * @return
- */
- public static long decr(String key, long by) {
- return redisTemplate.opsForValue().increment(appendKeyPrefix(key), -by);
- }
- /**
- * 递增操作
- *
- * @param key
- * @param by
- * @return
- */
- public static long incr(String key, long by) {
- return redisTemplate.opsForValue().increment(appendKeyPrefix(key), by);
- }
- /**
- * 获取double类型值
- *
- * @param key
- * @return
- */
- public static double getDouble(String key) {
- String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
- if (StringUtils.isNotBlank(value)) {
- return Double.valueOf(value);
- }
- return 0d;
- }
- /**
- * 设置double类型值
- *
- * @param key
- * @param value
- * @param seconds
- * 失效时间(秒)
- */
- public static void setDouble(String key, double value, long seconds) {
- stringRedisTemplate.opsForValue().set(appendKeyPrefix(key), String.valueOf(value));
- if (seconds > 0) {
- stringRedisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
- }
- }
- /**
- * 将map写入缓存
- *
- * @param key
- * @param map
- */
- public static <T> void setMap(String key, Map<String, T> map) {
- redisTemplate.opsForHash().putAll(appendKeyPrefix(key), map);
- }
- /**
- * 向key对应的map中添加缓存对象
- *
- * @param key
- * @param map
- */
- public static <T> void addMap(String key, Map<String, T> map) {
- redisTemplate.opsForHash().putAll(appendKeyPrefix(key), map);
- }
- /**
- * 向key对应的map中添加缓存对象
- *
- * @param key
- * cache对象key
- * @param field
- * map对应的key
- * @param value
- * 值
- */
- public static void addMap(String key, String field, String value) {
- redisTemplate.opsForHash().put(appendKeyPrefix(key), field, value);
- }
- /**
- * 向key对应的map中添加缓存对象
- *
- * @param key
- * cache对象key
- * @param field
- * map对应的key
- * @param obj
- * 对象
- */
- public static <T> void addMap(String key, String field, T obj) {
- redisTemplate.opsForHash().put(appendKeyPrefix(key), field, obj);
- }
- /**
- * 获取map缓存
- *
- * @param key
- * @param clazz
- * @return
- */
- public static <T> Map<String, T> mget(String key, Class<T> clazz) {
- BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(appendKeyPrefix(key));
- return boundHashOperations.entries();
- }
- /**
- * 获取map缓存中的某个对象
- *
- * @param key
- * @param field
- * @param clazz
- * @return
- */
- @SuppressWarnings("unchecked")
- public static <T> T getMapField(String key, String field, Class<T> clazz) {
- return (T) redisTemplate.boundHashOps(appendKeyPrefix(key)).get(field);
- }
- /**
- * 删除map中的某个对象
- *
- * @author lh
- * @date 2016年8月10日
- * @param key
- * map对应的key
- * @param field
- * map中该对象的key
- */
- public static void delMapField(String key, String... field) {
- redisTemplate.opsForHash().delete(appendKeyPrefix(key), field);
- }
- /**
- * 为哈希表key中的域field的值
- *
- * @param key
- * 键
- * @param field
- * map域
- * @param value
- * 增量值
- * @return
- */
- public static long hincr(String key, String field, long value) {
- return redisTemplate.opsForHash().increment(appendKeyPrefix(key), field, value);
- }
- public static void hset(String key, String field, Object value){
- redisTemplate.opsForHash().put(appendKeyPrefix(key), field, value);
- }
- public static Object hget(String key, String field){
- return redisTemplate.boundHashOps(appendKeyPrefix(key)).get(field);
- }
- public static void hdel(String key, String...fields){
- if (fields == null || fields.length == 0) {
- redisTemplate.delete(appendKeyPrefix(key));
- }else{
- redisTemplate.opsForHash().delete(appendKeyPrefix(key), fields);
- }
- }
- public static Long hlen(String key){
- return redisTemplate.boundHashOps(appendKeyPrefix(key)).size();
- }
- public static <T> Set<T> hkeys(String key){
- return (Set<T>)redisTemplate.boundHashOps(appendKeyPrefix(key)).keys();
- }
- public static <T> List<T> hvals(String key){
- return (List<T>)redisTemplate.boundHashOps(appendKeyPrefix(key)).values();
- }
- /**
- *
- * @param key
- * @param value
- * @param seconds
- * @return
- */
- public static boolean setnx(String key, String value, long seconds) {
- boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(appendKeyPrefix(key), value);
- if (seconds > 0) {
- redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
- }
- return flag;
- }
- /**
- * 指定缓存的失效时间
- *
- * @author FangJun
- * @date 2016年8月14日
- * @param key
- * 缓存KEY
- * @param seconds
- * 失效时间(秒)
- */
- public static void expire(String key, long seconds) {
- redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
- }
- /**
- * 指定缓存的失效时间
- *
- * @author FangJun
- * @date 2016年8月14日
- * @param key
- * 缓存KEY
- * @param seconds
- * 失效时间(秒)
- */
- public static void expire(String key, int seconds) {
- redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
- }
- /**
- * 添加set
- *
- * @param key
- * @param value
- */
- public static void sadd(String key, String... value) {
- redisTemplate.boundSetOps(appendKeyPrefix(key)).add(value);
- }
- /**
- * 删除set集合中的对象
- *
- * @param key
- * @param value
- */
- public static void srem(String key, String... value) {
- redisTemplate.boundSetOps(appendKeyPrefix(key)).remove(value);
- }
- /**
- * 判断key对应的缓存是否存在
- *
- * @param key
- * @return
- */
- public static boolean exists(String key) {
- return redisTemplate.hasKey(appendKeyPrefix(key));
- }
- /**
- * 模糊查询keys
- * @param pattern
- * @return
- */
- public static Set<String> keys(String pattern){
- return redisTemplate.keys(appendKeyPrefix(pattern));
- }
- /**
- * 合并数组为字符串
- * @param strings
- * @return
- */
- private static String merge(String...strings){
- if (strings == null || strings.length == 0) {
- return null;
- }
- StringBuffer sb = new StringBuffer();
- int len = strings.length;
- for (int i = 0; i < len; i++) {
- sb.append(strings[i]);
- if(len != i+1){
- sb.append(",");
- }
- }
- return sb.toString();
- }
- private static String appendKeyPrefix(String key){
- return redisKeyPrefix.concat(key);
- }
- }
4、增加AOP支持
- package com.huatech.support.redis;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import org.springframework.util.CollectionUtils;
- import com.huatech.common.annotation.CacheEvict;
- import com.huatech.common.annotation.Cacheable;
- import com.huatech.common.util.AopUtils;
- import com.huatech.common.util.CacheUtils;
- /**
- * 缓存操作切面
- * 注意:一个支持缓存的方法,在对象内部被调用是不会触发缓存功能的。
- * @author lh
- *
- */
- @Aspect
- @Component
- public class CacheAspect {
- /**
- * 缓存清除标识前缀
- */
- public static final String KEY_PREFIX_CACHE_EVICT="cacheEvict:interval:";
- public static final String REDIS_CACHE_SUPPORT = "1";
- private static final Logger LOGGER = LoggerFactory.getLogger(CacheAspect.class);
- @SuppressWarnings("rawtypes")
- @Autowired
- private RedisTemplate redisTemplate;
- @Value("${redis.cacheSupport}")
- private String redisCacheSupport;
- @Value("${redis.keyPrefix}")
- private String redisKeyPrefix;
- /**
- * 启用新的get方法,防止缓存被“击穿”
- * <p>
- * 击穿 :缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,
- * 这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- * 如何解决:业界比较常用的做法,是使用mutex。
- * 简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成
- * 功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行
- * load db的操作并回设缓存;否则,就重试整个get缓存的方法。
- * </p>
- * @param key
- * @param pjp
- * @param cache
- * @return
- * @throws Throwable
- */
- private Object get(final String key, final ProceedingJoinPoint pjp, final Cacheable cache) throws Throwable {
- @SuppressWarnings("unchecked")
- ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
- Object value = valueOper.get(key); // 从缓存获取数据
- if (value == null) { // 代表缓存值过期
- // 设置2min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
- String keynx = key.concat(":nx");
- if (CacheUtils.setnx(keynx, "1", 5)) { // 代表设置成功
- value = pjp.proceed();
- if (cache.expire() <= 0) { // 如果没有设置过期时间,则无限期缓存
- valueOper.set(key, value);
- } else { // 否则设置缓存时间
- valueOper.set(key, value, cache.expire(), TimeUnit.SECONDS);
- }
- CacheUtils.del(keynx);
- return value;
- } else { // 这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
- Thread.sleep(10);
- return get(key, pjp, cache); // 重试
- }
- } else {
- return value;
- }
- }
- /**
- * 添加缓存
- * @param pjp
- * @param cache
- * @return
- * @throws Throwable
- */
- @Around("@annotation(cache)")
- public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache)throws Throwable {
- if(REDIS_CACHE_SUPPORT.equals(redisCacheSupport)){
- String key = redisKeyPrefix.concat(AopUtils.getCacheKey(pjp, cache.key()));
- return get(key, pjp, cache);
- }
- return pjp.proceed();
- }
- @Around("@annotation(evict)")
- public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict)throws Throwable {
- if(REDIS_CACHE_SUPPORT.equals(redisCacheSupport)){
- Object value = pjp.proceed();
- //清除keys对应的缓存数据
- if (evict.keys() != null && evict.keys().length > 0) {
- for (String keyname : evict.keys()) {
- evictByKeyname(pjp, keyname,evict.interval());
- }
- }
- return value;
- }
- return pjp.proceed();
- }
- @SuppressWarnings("unchecked")
- private void evictByKeyname(final ProceedingJoinPoint pjp, final String keyname, long interval) {
- final String key = redisKeyPrefix.concat(AopUtils.getCacheKey(pjp, keyname));
- //操作间隔判断
- if (interval != 0) {
- final String intervalKey = KEY_PREFIX_CACHE_EVICT + key;
- if (CacheUtils.incr(intervalKey, 1) > 1) {
- return;
- }
- CacheUtils.expire(intervalKey, interval);
- }
- LOGGER.info("cacheEvict, key={}", key);
- //使用redisTemplate操作缓存
- if (keyname.equals(key)) {// 支持批量删除
- Set<String> keys = redisTemplate.keys(key.concat("*"));
- if (!CollectionUtils.isEmpty(keys)) {
- redisTemplate.delete(keys);
- }
- } else {
- redisTemplate.delete(key);
- }
- }
- }
5、扩展spring-data-redis,简化配置
- package com.huatech.support.redis;
- import static org.springframework.util.Assert.isTrue;
- import static org.springframework.util.Assert.notNull;
- import static org.springframework.util.StringUtils.split;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.data.redis.connection.RedisClusterConfiguration;
- import org.springframework.data.redis.connection.RedisNode;
- /**
- * <p>RedisCluster配置</p>
- * <p>用于简化spring-data-redis配置</p>
- * @author lh
- * @version 3.0
- * @since 2017-4-5
- *
- */
- public class RedisClusterConfig extends RedisClusterConfiguration{
- public RedisClusterConfig(String nodes, Integer maxRedirects) {
- super();
- initNodes(nodes);
- setMaxRedirects(maxRedirects);
- }
- private void initNodes(String nodes){
- if(StringUtils.isBlank(nodes)){
- throw new RuntimeException("nodes can not be empty!");
- }
- String[] hostAndPorts = nodes.split(",");
- for (String hostAndPort : hostAndPorts) {
- addClusterNode(readHostAndPortFromString(hostAndPort));
- }
- }
- private RedisNode readHostAndPortFromString(String hostAndPort) {
- String[] args = split(hostAndPort, ":");
- notNull(args, "HostAndPort need to be seperated by ':'.");
- isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
- return new RedisNode(args[0], Integer.valueOf(args[1]).intValue());
- }
- }
- package com.huatech.support.redis;
- import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
- /**
- * Jedis Single Connection factory
- * @author lh
- *
- */
- public class RedisSingleConnFactory extends JedisConnectionFactory {
- public RedisSingleConnFactory(String address,int timeout, String password, redis.clients.jedis.JedisPoolConfig poolConfig) {
- String[] hostAndPorts = address.split(":");
- setHostName(hostAndPorts[0]);
- setPort(Integer.valueOf(hostAndPorts[1]));
- setTimeout(timeout);
- setPassword(password);
- setPoolConfig(poolConfig);
- }
- public RedisSingleConnFactory(String address, String password, redis.clients.jedis.JedisPoolConfig poolConfig) {
- String[] hostAndPorts = address.split(":");
- setHostName(hostAndPorts[0]);
- setPort(Integer.valueOf(hostAndPorts[1]));
- setPassword(password);
- setPoolConfig(poolConfig);
- }
- public RedisSingleConnFactory(String address, redis.clients.jedis.JedisPoolConfig poolConfig) {
- String[] hostAndPorts = address.split(":");
- setHostName(hostAndPorts[0]);
- setPort(Integer.valueOf(hostAndPorts[1]));
- setPoolConfig(poolConfig);
- }
- }
6、添加redis配置文件:redis.properties
- #redis settings
- redis.cacheSupport=1
- redis.keyPrefix=bds_
- #redis pool settings
- redis.pool.maxTotal=60000
- redis.pool.maxIdle=300
- redis.pool.testOnBorrow=true
- #redis 单机配置
- spring.redis.single.node=172.16.90.30:6379
- spring.redis.single.timeout=8000
- spring.redis.single.password=redis
- #redis 集群
- spring.redis.cluster.maxRedirects=3
- spring.redis.cluster.nodes=192.168.42.128:6379,192.168.42.128:6380,192.168.42.129:6379,192.168.42.129:6380,192.168.42.130:6379,192.168.42.130:6380
7、在applicationContext.xml文件中添加redis配置
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" >
- <description>Redis Configuration</description>
- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="order" value="1" />
- <property name="ignoreUnresolvablePlaceholders" value="true" />
- <property name="locations">
- <list>
- <value>classpath:/mybatis/jdbc.properties</value>
- <value>classpath:/config/redis.properties</value>
- </list>
- </property>
- </bean>
- <aop:aspectj-autoproxy/>
- <context:component-scan base-package="com.huatech.support"/>
- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
- <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数 -->
- <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 -->
- <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
- </bean>
- <!-- ======================单机配置 start ====================== -->
- <!-- spring-data-redis 单机配置 -->
- <bean id="jedisConnFactory" class="com.huatech.support.redis.RedisSingleConnFactory" >
- <constructor-arg name="address" value="${spring.redis.single.node}"/>
- <constructor-arg name="timeout" type="int" value="${spring.redis.single.timeout}" />
- <constructor-arg name="password" value="${spring.redis.single.password}" />
- <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
- </bean>
- <!-- ======================单机配置 end ====================== -->
- <!-- ======================cluster配置 start ====================== -->
- <!-- spring-data-redis-cluster
- <bean id="redisClusterConfiguration" class="com.huatech.support.redis.RedisClusterConfig" >
- <constructor-arg value="${spring.redis.cluster.nodes}"/>
- <constructor-arg value="${spring.redis.cluster.maxRedirects}" />
- </bean>
- <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true">
- <constructor-arg ref="redisClusterConfiguration" />
- <constructor-arg ref="jedisPoolConfig" />
- </bean>
- -->
- <!-- ======================cluster配置 end ====================== -->
- <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
- <bean id ="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
- <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
- p:connectionFactory-ref="jedisConnFactory" />
- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
- p:connectionFactory-ref="jedisConnFactory"
- p:keySerializer-ref="stringRedisSerializer"
- p:hashKeySerializer-ref="stringRedisSerializer"
- p:valueSerializer-ref="jdkSerializationRedisSerializer" />
- </beans>
8、系统中添加缓存支持
- @Override
- @Cacheable(key="sc:code:{configCode}", expire = 0)
- public SystemConfig findByConfigCode(String configCode) {
- return systemConfigDao.findByConfigCode(configCode);
- }
9、简单测试
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- import com.rd.bds.core.common.util.CacheUtils;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { "classpath:/spring/spring-config.xml", "classpath:/spring/spring-redis.xml" })
- public class CacheUtilsTest {
- @Test
- public void testString()throws Exception{
- String key = "email";
- CacheUtils.set(key, "lh@erongdu.com", 20);
- System.out.println(key+":"+CacheUtils.getStr(key));
- Thread.sleep(1500);
- System.out.println(key+":"+CacheUtils.getStr(key));
- Thread.sleep(1500);
- System.out.println(key+":"+CacheUtils.getStr(key));
- }
- }
- import javax.annotation.Resource;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- import com.rd.bds.core.service.SystemConfigService;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { "classpath:/spring/spring-config.xml", "classpath:/spring/spring-redis.xml" })
- public class SystemConfigServiceImplTest {
- @Resource
- private SystemConfigService systemConfigService;
- @Test
- public void test()throws Exception{
- systemConfigService.findByConfigCode("invest_award_limit");
- Thread.sleep(100L);
- systemConfigService.findByConfigCode("invest_award_limit");
- Thread.sleep(100L);
- systemConfigService.findByConfigCode("invest_award_limit");
- }
- }
spring-data-redis分布式的更多相关文章
- spring data redis RedisTemplate操作redis相关用法
http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...
- spring mvc Spring Data Redis RedisTemplate [转]
http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...
- Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解
一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...
- Spring Data Redis—Pub/Sub(附Web项目源码)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring data redis的一个bug
起因 前两天上线了一个新功能,导致线上业务的缓存总是无法更新,报错也是非常奇怪,redis.clients.jedis.exceptions.JedisConnectionException: Unk ...
- Spring Data Redis—Pub/Sub(附Web项目源码) (转)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- spring data redis 理解
前言 Spring Data Redis project,应用了Spring概念来开发使用键值形式的数据存储的解决方案.我们(官方)提供了一个 "template" ,这是一个高级 ...
- Spring Data Redis 详解及实战一文搞定
SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...
- Spring Data Redis 让 NoSQL 快如闪电(2)
[编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...
- Spring Data Redis 让 NoSQL 快如闪电 (1)
[编者按]本文作者为 Xinyu Liu,详细介绍了 Redis 的特性,并辅之以丰富的用例.在本文的第一部分,将重点概述 Redis 的方方面面.文章系国内 ITOM 管理平台 OneAPM 编译呈 ...
随机推荐
- Bomb Game HDU - 3622(二分最小值最大化)
题意: 就是给出n对坐标,每对只能选一个,以选出来的点为圆心,半径自定义,画圆,而这些圆不能覆盖,求半径最小的圆的最大值 解析: 看到最x值最x化,那二分变为判定性问题,然后...然后我就没想到... ...
- pfSense用户界面汉化翻译教程
pfSense用户界面汉化翻译教程 来源 https://blog.51cto.com/fxn2025/2087182 为了记录自己的汉化过程,同时也为了方便网友自己制作汉化版本,我把自己汉化pfSe ...
- 【LOJ#6060】Set(线性基)
[LOJ#6060]Set(线性基) 题面 LOJ 题解 好题啊QwQ. 首先\(x1\oplus x2=s\)是定值.而\(s\)中假设某一位上是\(1\),则\(x1,x2\)上必定有一个是\(1 ...
- [luogu4139]上帝与集合的正确用法【欧拉定理+扩展欧拉定理】
题目大意 让你求\(2^{2^{2^{\cdots}}}(mod)P\)的值. 前置知识 知识1:无限次幂怎么解决 让我们先来看一道全国数学竞赛的一道水题: 让你求解:\(x^{x^{x^{\cdot ...
- BZOJ 2839: 集合计数 解题报告
BZOJ 2839: 集合计数 Description 一个有\(N\)个元素的集合有\(2^N\)个不同子集(包含空集),现在要在这\(2^N\)个集合中取出若干集合(至少一个),使得 它们的交集的 ...
- CentOS装个NTP时间同步服务器
服务端: driftfile /var/lib/ntp/drift restrict default nomodify notrap nopeer noquery restrict 127.0.0.1 ...
- linux复制文件到一个不存在的文件夹
复制文件到一个不存在的文件夹时,会报错 cp -f aaa /home/admin/.m2/cp: 无法创建普通文件"/home/admin/.m2/": 是一个目录 解决的方式: ...
- Tutorial: Build a Spring WebMVC App with Primefaces
Tutorial: Build a Spring WebMVC App with Primefaces by Team Stormpath | September 7, 2016 | Java Pri ...
- GNOME禁用GDM中night-light功能
Night-light feature is enabled also in GDM screen, see here : https://bugzilla.gnome.org/show_bug.cg ...
- 百度地图API,展示地图和添加控件
1.申请百度账号和AK 点我申请 2.准备页面 根据HTML标准,每一份HTML文档都应该声明正确的文档类型,我们建议您使用最新的符合HTML5规范的文档声明: <!DOCTYPE html&g ...