`
hbxflihua
  • 浏览: 676200 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

通过spring-data-redis实现redis分布式缓存(增强版)

阅读更多

 

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");
		
	}

}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics