1、引入spring-data-redis依赖的jar 包
Xml代码
org.springframework.data
spring-data-redis
1.7.1.RELEASE
spring-tx
org.springframework
spring-context-support
org.springframework
spring-core
org.springframework
spring-aop
org.springframework
spring-context
org.springframework
redis.clients
jedis
2.8.1
2、添加缓存注解
Java代码
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;
}
Java代码
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、添加缓存操作工具类
Java代码
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
if(i
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
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static Class getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*
* 如public UserDao extends HibernateDao
*
* @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
|| einstanceof 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 getClassField(Class clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
Map fieldMap =new HashMap();
Map superFieldMap =new HashMap();
for (Field field : declaredFields) {
fieldMap.put(field.getName(), field);
}
if (clazz.getSuperclass() != null) {
superFieldMap = getClassField(clazz.getSuperclass());
}
fieldMap.putAll(superFieldMap);
return fieldMap;
}
}
Java代码
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() { }
/**
*
获取方法的参数名
*
* @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;
}
/**
*
比较参数类型是否一致
*
* @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 getMatcher(String regex, String source) {
List 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的位置
(?
* @param managers
* @return
*/
public static List 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 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;
}
}
Java代码
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();
}
}
Java代码
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 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() {
}
/**
* 删除缓存
* 根据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));
}
}
/**
* 批量删除
* (该操作会执行模糊查询,请尽量不要使用,以免影响性能或误删)
*
* @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;
}
/**
* 获取缓存
* 注:基本数据类型(Character除外),请直接使用get(String key, Class clazz)取值
*
* @param key
* @return
*/
public static Object getObj(String key) {
return redisTemplate.boundValueOps(appendKeyPrefix(key)).get();
}
/**
* 获取缓存
* 注:java 8种基本类型的数据请直接使用get(String key, Class 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;
}
/**
* 获取缓存
* 注:慎用java基本数据类型进行转换(可能会出现空值,转换报错)
*
* @param key
* key
* @param clazz
* 类型
* @return
*/
@SuppressWarnings("unchecked")
public static T get(String key, Class 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 void setMap(String key, Map map) {
redisTemplate.opsForHash().putAll(appendKeyPrefix(key), map);
}
/**
* 向key对应的map中添加缓存对象
*
* @param key
* @param map
*/
public static void addMap(String key, Map 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 void addMap(String key, String field, T obj) {
redisTemplate.opsForHash().put(appendKeyPrefix(key), field, obj);
}
/**
* 获取map缓存
*
* @param key
* @param clazz
* @return
*/
public static Map mget(String key, Class clazz) {
BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(appendKeyPrefix(key));
return boundHashOperations.entries();
}
/**
* 获取map缓存中的某个对象
*
* @param key
* @param field
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public static T getMapField(String key, String field, Class 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 Set hkeys(String key){
return (Set)redisTemplate.boundHashOps(appendKeyPrefix(key)).keys();
}
public static List hvals(String key){
return (List)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 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支持
Java代码
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方法,防止缓存被“击穿”
*
* 击穿 :缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,
* 这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
* 如何解决:业界比较常用的做法,是使用mutex。
* 简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成
* 功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行
* load db的操作并回设缓存;否则,就重试整个get缓存的方法。
*
* @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 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 keys = redisTemplate.keys(key.concat("*"));
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}else {
redisTemplate.delete(key);
}
}
}
5、扩展spring-data-redis,简化配置
Java代码
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;
/**
*
RedisCluster配置
*
用于简化spring-data-redis配置
* @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());
}
}
Java代码
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
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代码
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">
Redis Configuration
classpath:/mybatis/jdbc.properties
classpath:/config/redis.properties
-->
p:connectionFactory-ref="jedisConnFactory" />
p:connectionFactory-ref="jedisConnFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
p:valueSerializer-ref="jdkSerializationRedisSerializer" />
8、系统中添加缓存支持
Java代码
@Override
@Cacheable(key="sc:code:{configCode}", expire = 0)
public SystemConfig findByConfigCode(String configCode) {
return systemConfigDao.findByConfigCode(configCode);
}
9、简单测试
Java代码
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,"[email protected]", 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));
}
}
Java代码
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");
}
}
如果你也想在IT行业拿高薪,可以参加我们的训练营课程,选择最适合自己的课程学习,技术大牛亲授,7个月后,进入名企拿高薪。我们的课程内容有:Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点。如果你想拿高薪的,想学习的,想就业前景好的,想跟别人竞争能取得优势的,想进阿里面试但担心面试不过的,你都可以来,群号为: 454377428
注:加群要求
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。
5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!
6.小号或者小白之类加群一律不给过,谢谢。
目标已经有了,下面就看行动了!记住:学习永远是自己的事情,你不学时间也不会多,你学了有时候却能够使用自己学到的知识换得更多自由自在的美好时光!时间是生命的基本组成部分,也是万物存在的根本尺度,我们的时间在那里我们的生活就在那里!我们价值也将在那里提升或消弭!Java程序员,加油吧