本文转载http://hbxflihua.iteye.com/blog/2383495

1、引入spring-data-redis依赖的jar 包

  1. <dependency>
  2. <groupId>org.springframework.data</groupId>
  3. <artifactId>spring-data-redis</artifactId>
  4. <version>1.7.1.RELEASE</version>
  5. <exclusions>
  6. <exclusion>
  7. <artifactId>spring-tx</artifactId>
  8. <groupId>org.springframework</groupId>
  9. </exclusion>
  10. <exclusion>
  11. <artifactId>spring-context-support</artifactId>
  12. <groupId>org.springframework</groupId>
  13. </exclusion>
  14. <exclusion>
  15. <artifactId>spring-core</artifactId>
  16. <groupId>org.springframework</groupId>
  17. </exclusion>
  18. <exclusion>
  19. <artifactId>spring-aop</artifactId>
  20. <groupId>org.springframework</groupId>
  21. </exclusion>
  22. <exclusion>
  23. <artifactId>spring-context</artifactId>
  24. <groupId>org.springframework</groupId>
  25. </exclusion>
  26. </exclusions>
  27. </dependency>
  28. <dependency>
  29. <groupId>redis.clients</groupId>
  30. <artifactId>jedis</artifactId>
  31. <version>2.8.1</version>
  32. </dependency>

2、添加缓存注解

  1. package com.huatech.common.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. /**
  9. * 添加缓存
  10. * @author lh
  11. *
  12. */
  13. @Target({ElementType.METHOD})
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Inherited
  16. @Documented
  17. public @interface Cacheable {
  18. /**
  19. * 缓存key
  20. * @return
  21. */
  22. public String key() default "";
  23. /**
  24. * 缓存时效,默认无限期
  25. * @return
  26. */
  27. public long expire() default 0L;
  28. }
  1. package com.huatech.common.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Inherited;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8. /**
  9. * 缓存清除
  10. * @author lh
  11. * @version 3.0
  12. * @since 2016-8-28
  13. *
  14. */
  15. @Target({ElementType.METHOD})
  16. @Retention(RetentionPolicy.RUNTIME)
  17. @Inherited
  18. @Documented
  19. public @interface CacheEvict {
  20. /**
  21. * 缓存key数组
  22. * @return
  23. */
  24. String[] keys() default "";
  25. /**
  26. * 操作之间的缓存时间(秒)
  27. * @author  FangJun
  28. * @date 2016年9月9日
  29. * @return 默认0,不做限制
  30. */
  31. long interval() default 0;
  32. }

3、添加缓存操作工具类

  1. package com.huatech.common.util;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Modifier;
  6. import java.lang.reflect.ParameterizedType;
  7. import java.lang.reflect.Type;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. import org.apache.commons.lang3.ArrayUtils;
  11. import org.apache.commons.lang3.StringUtils;
  12. import org.apache.commons.lang3.Validate;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. @SuppressWarnings("rawtypes")
  16. public class ReflectionUtils {
  17. private static final String SETTER_PREFIX = "set";
  18. private static final String GETTER_PREFIX = "get";
  19. private static final String CGLIB_CLASS_SEPARATOR = "$$";
  20. private static Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);
  21. public static Object invokeGetMethod(Class<?> claszz, Object o, String name) {
  22. Object ret = null;
  23. try {
  24. Method method = claszz.getMethod("get" +  StringUtil.firstCharUpperCase(name));
  25. ret = method.invoke(o);
  26. } catch (Exception e) {
  27. logger.error(e.getMessage(),e);
  28. }
  29. return ret;
  30. }
  31. /**
  32. * 调用Getter方法.
  33. * 支持多级,如:对象名.对象名.方法
  34. */
  35. public static Object invokeGetter(Object obj, String propertyName) {
  36. Object object = obj;
  37. for (String name : StringUtils.split(propertyName, ".")){
  38. String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
  39. object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
  40. }
  41. return object;
  42. }
  43. /**
  44. * 调用Setter方法, 仅匹配方法名。
  45. * 支持多级,如:对象名.对象名.方法
  46. */
  47. public static void invokeSetter(Object obj, String propertyName, Object value) {
  48. Object object = obj;
  49. String[] names = StringUtils.split(propertyName, ".");
  50. for (int i=0; i<names.length; i++){
  51. if(i<names.length-1){
  52. String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
  53. object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
  54. }else{
  55. String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
  56. invokeMethodByName(object, setterMethodName, new Object[] { value });
  57. }
  58. }
  59. }
  60. /**
  61. * 调用Setter方法(赋值)
  62. * @param claszz
  63. * @param o
  64. * @param name
  65. * @param argType
  66. * @param args
  67. * @return
  68. */
  69. public static Object invokeSetter(Class<?> claszz, Object o, String name, Class<?> argType, Object args) {
  70. Object ret = null;
  71. try {
  72. // 非 常量 进行反射
  73. if (!checkModifiers(claszz, name)) {
  74. Method method = claszz.getMethod("set" + StringUtil.firstCharUpperCase(name), new Class[] { argType });
  75. ret = method.invoke(o, new Object[] { args });
  76. }
  77. } catch (Exception e) {
  78. logger.error("claszz:{},name:{},argType:{},args:{}",claszz,name,argType, args);
  79. }
  80. return ret;
  81. }
  82. /**
  83. * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
  84. */
  85. public static Object getFieldValue(final Object obj, final String fieldName) {
  86. Field field = getAccessibleField(obj, fieldName);
  87. if (field == null) {
  88. throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
  89. }
  90. Object result = null;
  91. try {
  92. result = field.get(obj);
  93. } catch (IllegalAccessException e) {
  94. logger.error("不可能抛出的异常{}", e.getMessage());
  95. }
  96. return result;
  97. }
  98. /**
  99. * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
  100. */
  101. public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
  102. Field field = getAccessibleField(obj, fieldName);
  103. if (field == null) {
  104. throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
  105. }
  106. try {
  107. field.set(obj, value);
  108. } catch (IllegalAccessException e) {
  109. logger.error("不可能抛出的异常:{}", e.getMessage());
  110. }
  111. }
  112. /**
  113. * 直接调用对象方法, 无视private/protected修饰符.
  114. * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
  115. * 同时匹配方法名+参数类型,
  116. */
  117. public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
  118. final Object[] args) {
  119. Method method = getAccessibleMethod(obj, methodName, parameterTypes);
  120. if (method == null) {
  121. throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
  122. }
  123. try {
  124. return method.invoke(obj, args);
  125. } catch (Exception e) {
  126. throw convertReflectionExceptionToUnchecked(e);
  127. }
  128. }
  129. /**
  130. * 直接调用对象方法, 无视private/protected修饰符,
  131. * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
  132. * 只匹配函数名,如果有多个同名函数调用第一个。
  133. */
  134. public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
  135. Method method = getAccessibleMethodByName(obj, methodName);
  136. if (method == null) {
  137. throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
  138. }
  139. try {
  140. return method.invoke(obj, args);
  141. } catch (Exception e) {
  142. throw convertReflectionExceptionToUnchecked(e);
  143. }
  144. }
  145. /**
  146. * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
  147. *
  148. * 如向上转型到Object仍无法找到, 返回null.
  149. */
  150. public static Field getAccessibleField(final Object obj, final String fieldName) {
  151. Validate.notNull(obj, "object can't be null");
  152. Validate.notBlank(fieldName, "fieldName can't be blank");
  153. for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
  154. try {
  155. Field field = superClass.getDeclaredField(fieldName);
  156. makeAccessible(field);
  157. return field;
  158. } catch (NoSuchFieldException e) {//NOSONAR
  159. // Field不在当前类定义,继续向上转型
  160. continue;// new add
  161. }
  162. }
  163. return null;
  164. }
  165. /**
  166. * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
  167. * 如向上转型到Object仍无法找到, 返回null.
  168. * 匹配函数名+参数类型。
  169. *
  170. * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
  171. */
  172. public static Method getAccessibleMethod(final Object obj, final String methodName,
  173. final Class<?>... parameterTypes) {
  174. Validate.notNull(obj, "object can't be null");
  175. Validate.notBlank(methodName, "methodName can't be blank");
  176. for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
  177. try {
  178. Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
  179. makeAccessible(method);
  180. return method;
  181. } catch (NoSuchMethodException e) {
  182. // Method不在当前类定义,继续向上转型
  183. continue;// new add
  184. }
  185. }
  186. return null;
  187. }
  188. /**
  189. * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
  190. * 如向上转型到Object仍无法找到, 返回null.
  191. * 只匹配函数名。
  192. *
  193. * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
  194. */
  195. public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
  196. Validate.notNull(obj, "object can't be null");
  197. Validate.notBlank(methodName, "methodName can't be blank");
  198. for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
  199. Method[] methods = searchType.getDeclaredMethods();
  200. for (Method method : methods) {
  201. if (method.getName().equals(methodName)) {
  202. makeAccessible(method);
  203. return method;
  204. }
  205. }
  206. }
  207. return null;
  208. }
  209. /**
  210. * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
  211. */
  212. public static void makeAccessible(Method method) {
  213. if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
  214. && !method.isAccessible()) {
  215. method.setAccessible(true);
  216. }
  217. }
  218. /**
  219. * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
  220. */
  221. public static void makeAccessible(Field field) {
  222. if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
  223. .isFinal(field.getModifiers())) && !field.isAccessible()) {
  224. field.setAccessible(true);
  225. }
  226. }
  227. /**
  228. * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
  229. * 如无法找到, 返回Object.class.
  230. * eg.
  231. * public UserDao extends HibernateDao<User>
  232. *
  233. * @param clazz The class to introspect
  234. * @return the first generic declaration, or Object.class if cannot be determined
  235. */
  236. @SuppressWarnings("unchecked")
  237. public static <T> Class<T> getClassGenricType(final Class clazz) {
  238. return getClassGenricType(clazz, 0);
  239. }
  240. /**
  241. * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
  242. * 如无法找到, 返回Object.class.
  243. *
  244. * 如public UserDao extends HibernateDao<User,Long>
  245. *
  246. * @param clazz clazz The class to introspect
  247. * @param index the Index of the generic ddeclaration,start from 0.
  248. * @return the index generic declaration, or Object.class if cannot be determined
  249. */
  250. public static Class getClassGenricType(final Class clazz, final int index) {
  251. Type genType = clazz.getGenericSuperclass();
  252. if (!(genType instanceof ParameterizedType)) {
  253. logger.warn("{}'s superclass not ParameterizedType",clazz.getSimpleName());
  254. return Object.class;
  255. }
  256. Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
  257. if (index >= params.length || index < 0) {
  258. logger.warn("Index: {}, Size of {}'s Parameterized Type: {}",index,clazz.getSimpleName(), params.length);
  259. return Object.class;
  260. }
  261. if (!(params[index] instanceof Class)) {
  262. logger.warn(" {} not set the actual class on superclass generic parameter",clazz.getSimpleName());
  263. return Object.class;
  264. }
  265. return (Class) params[index];
  266. }
  267. public static Class<?> getUserClass(Object instance) {
  268. if(instance == null){
  269. throw new RuntimeException("Instance must not be null");
  270. }
  271. Class clazz = instance.getClass();
  272. if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
  273. Class<?> superClass = clazz.getSuperclass();
  274. if (superClass != null && !Object.class.equals(superClass)) {
  275. return superClass;
  276. }
  277. }
  278. return clazz;
  279. }
  280. /**
  281. * 取得类中某个Field的类型名称
  282. * @param clazz
  283. * @param fieldName
  284. * @return
  285. */
  286. public static String getFieldTypeName(final Class clazz, String fieldName) {
  287. Field field = null;
  288. Class tclazz = clazz;
  289. do {
  290. try {
  291. field = tclazz.getDeclaredField(fieldName);
  292. } catch (NoSuchFieldException e) {
  293. tclazz = clazz.getSuperclass();
  294. } catch (SecurityException e) {
  295. }
  296. } while (tclazz != Object.class && field == null);
  297. return (field == null)?null:field.getType().getSimpleName();
  298. }
  299. /**
  300. * 将反射时的checked exception转换为unchecked exception.
  301. */
  302. public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
  303. if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
  304. || e instanceof NoSuchMethodException) {
  305. return new IllegalArgumentException(e);
  306. } else if (e instanceof InvocationTargetException) {
  307. return new RuntimeException(((InvocationTargetException) e).getTargetException());
  308. } else if (e instanceof RuntimeException) {
  309. return (RuntimeException) e;
  310. }
  311. return new RuntimeException("Unexpected Checked Exception.", e);
  312. }
  313. /**
  314. * object 属性名称及属性值组装为String字符串。
  315. * 组装规则:
  316. * field.name1=field.value1&field.name2=field.value2 ...
  317. * @param object
  318. * @return
  319. */
  320. public static String objToString(Object object) {
  321. Class<?> clazz = object.getClass();
  322. Field[] fss = new Field[0];
  323. for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
  324. try {
  325. Field[] fs = clazz.getDeclaredFields();
  326. fss = ArrayUtils.addAll(fss, fs);
  327. } catch (Exception e) {
  328. // 这里异常不能抛出去。
  329. // 如果这里的异常打印或者往外抛,就不会执行clazz = clazz.getSuperclass(),
  330. // 最后就不会进入到父类中了
  331. }
  332. }
  333. StringBuffer sb = new StringBuffer(50);
  334. for (Field f : fss) {
  335. // 反射对象中String类型,且不为常量的字段
  336. if (String.class.equals(f.getType()) && !isConstant(f.getModifiers())) {
  337. String fieldName = f.getName();
  338. Object o = invokeGetMethod(f.getDeclaringClass(), object, fieldName);
  339. String value = null==o?"":o.toString();
  340. if (value == "") {
  341. continue;
  342. }
  343. sb.append(fieldName + "=" + value + "&");
  344. }
  345. }
  346. logger.info("请求参数:"+sb.toString());
  347. return sb.toString();
  348. }
  349. /**
  350. * 是否为常量
  351. * @param modifiers
  352. * @return 常量返回true,非常量返回false
  353. */
  354. private static boolean isConstant(int modifiers) {
  355. // static 和 final修饰
  356. if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
  357. return true;
  358. }
  359. return false;
  360. }
  361. /**
  362. * 校验参数类型
  363. * 目前只校验是否为 常量
  364. * @param claszz
  365. * @param name
  366. * @return 常量返回true,非常量返回false
  367. */
  368. private static boolean checkModifiers(Class<?> claszz, String name) {
  369. try {
  370. Field field = claszz.getField(name);
  371. if (isConstant(field.getModifiers())) {
  372. return true;
  373. }
  374. } catch (NoSuchFieldException | SecurityException e) {
  375. return false;
  376. }
  377. return false;
  378. }
  379. /**
  380. * 取得属性
  381. * @param clazz
  382. * @return
  383. */
  384. public static Map<String, Field> getClassField(Class<?> clazz) {
  385. Field[] declaredFields = clazz.getDeclaredFields();
  386. Map<String, Field> fieldMap = new HashMap<String, Field>();
  387. Map<String, Field> superFieldMap = new HashMap<String, Field>();
  388. for (Field field : declaredFields) {
  389. fieldMap.put(field.getName(), field);
  390. }
  391. if (clazz.getSuperclass() != null) {
  392. superFieldMap = getClassField(clazz.getSuperclass());
  393. }
  394. fieldMap.putAll(superFieldMap);
  395. return fieldMap;
  396. }
  397. }
  1. package com.huatech.common.util;
  2. import org.apache.commons.lang3.StringUtils;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.Signature;
  5. import org.aspectj.lang.reflect.MethodSignature;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.asm.*;
  9. import org.springframework.util.CollectionUtils;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.lang.reflect.Method;
  13. import java.lang.reflect.Modifier;
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. import java.util.regex.Matcher;
  17. import java.util.regex.Pattern;
  18. /**
  19. * 切面编程工具类
  20. * @author lh
  21. * @version 3.0
  22. * @since 2016-8-26
  23. */
  24. public class AopUtils {
  25. private static final Logger LOGGER = LoggerFactory.getLogger(AopUtils.class);
  26. private static final String DESC_DOUBLE = "D";
  27. private static final String DESC_SHORT = "J";
  28. private AopUtils() {    }
  29. /**
  30. * <p>获取方法的参数名</p>
  31. *
  32. * @param m
  33. * @return
  34. */
  35. public static String[] getMethodParamNames(final Method m) {
  36. final String[] paramNames = new String[m.getParameterTypes().length];
  37. final String n = m.getDeclaringClass().getName();
  38. final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  39. String className = m.getDeclaringClass().getSimpleName();
  40. ClassReader cr = null;
  41. InputStream resourceAsStream = null;
  42. try {
  43. resourceAsStream = Class.forName(n).getResourceAsStream(className + ".class");
  44. cr = new ClassReader(resourceAsStream);
  45. } catch (IOException | ClassNotFoundException e) {
  46. LOGGER.warn(e.getMessage(), e);
  47. } finally {
  48. if (resourceAsStream != null) {
  49. try {
  50. resourceAsStream.close();
  51. } catch (IOException e) {
  52. LOGGER.warn(e.getMessage(), e);
  53. }
  54. }
  55. }
  56. if (cr != null) {
  57. cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
  58. @Override
  59. public MethodVisitor visitMethod(final int access,
  60. final String name, final String desc,
  61. final String signature, final String[] exceptions) {
  62. final Type[] args = Type.getArgumentTypes(desc);
  63. // 方法名相同并且参数个数相同
  64. if (!name.equals(m.getName())
  65. || !sameType(args, m.getParameterTypes())) {
  66. return super.visitMethod(access, name, desc, signature,
  67. exceptions);
  68. }
  69. MethodVisitor v = cv.visitMethod(access, name, desc, signature,
  70. exceptions);
  71. return new MethodVisitor(Opcodes.ASM4, v) {
  72. int fixCount = 0;//步长修正计数器
  73. @Override
  74. public void visitLocalVariable(String name, String desc,
  75. String signature, Label start, Label end, int index) {
  76. int i = index - 1;
  77. // 如果是静态方法,则第一就是参数
  78. // 如果不是静态方法,则第一个是"this",然后才是方法的参数
  79. if (Modifier.isStatic(m.getModifiers())) {
  80. i = index;
  81. }
  82. if (i > fixCount) {
  83. i -= fixCount;
  84. }
  85. if(desc.equals(DESC_SHORT) || desc.equals(DESC_DOUBLE)){
  86. fixCount++;
  87. }
  88. if (i >= 0 && i < paramNames.length) {
  89. paramNames[i] = name;
  90. }
  91. super.visitLocalVariable(name, desc, signature, start,
  92. end, index);
  93. }
  94. };
  95. }
  96. }, 0);
  97. }
  98. return paramNames;
  99. }
  100. /**
  101. * <p>比较参数类型是否一致</p>
  102. *
  103. * @param types   asm的类型({@link Type})
  104. * @param clazzes java 类型({@link Class})
  105. * @return
  106. */
  107. private static boolean sameType(Type[] types, Class<?>[] clazzes) {
  108. // 个数不同
  109. if (types.length != clazzes.length) {
  110. return false;
  111. }
  112. for (int i = 0; i < types.length; i++) {
  113. if (!Type.getType(clazzes[i]).equals(types[i])) {
  114. return false;
  115. }
  116. }
  117. return true;
  118. }
  119. /**
  120. * 取得切面调用的方法
  121. * @param pjp
  122. * @return
  123. */
  124. public static Method getMethod(ProceedingJoinPoint pjp){
  125. Signature sig = pjp.getSignature();
  126. if (!(sig instanceof MethodSignature)) {
  127. throw new IllegalArgumentException("该注解只能用于方法");
  128. }
  129. MethodSignature msig = (MethodSignature) sig;
  130. Object target = pjp.getTarget();
  131. Method currentMethod = null;
  132. try {
  133. currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
  134. } catch (NoSuchMethodException | SecurityException e) {
  135. LOGGER.warn(e.getMessage(), e);
  136. }
  137. return currentMethod;
  138. }
  139. public static List<String> getMatcher(String regex, String source) {
  140. List<String> list = new ArrayList<>();
  141. Pattern pattern = Pattern.compile(regex);
  142. Matcher matcher = pattern.matcher(source);
  143. while (matcher.find()) {
  144. list.add(matcher.group());
  145. }
  146. return list;
  147. }
  148. /**
  149. * 取得注解参数
  150. (?=exp) 匹配exp前面的位置
  151. (?<=exp) 匹配exp后面的位置
  152. (?!exp) 匹配后面跟的不是exp的位置
  153. (?<!exp) 匹配前面不是exp的位置
  154. * @param managers
  155. * @return
  156. */
  157. public static List<String> getAnnoParams(String source){
  158. String regex = "(?<=\\{)(.+?)(?=\\})";
  159. return getMatcher(regex, source);
  160. }
  161. /**
  162. * 获取缓存的key值
  163. *
  164. * @param pjp
  165. * @param key
  166. * @return
  167. */
  168. public static String getCacheKey(final ProceedingJoinPoint pjp, final String key) {
  169. StringBuilder buf = new StringBuilder();
  170. final Object[] args = pjp.getArgs();
  171. if(StringUtils.isNotBlank(key)){
  172. buf.append(key);
  173. List<String> annoParamNames = AopUtils.getAnnoParams(key);
  174. String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
  175. if(!CollectionUtils.isEmpty(annoParamNames)){
  176. for (String ap : annoParamNames) {
  177. buf = replaceParam(buf, args, methodParamNames, ap);
  178. }
  179. }
  180. }else{
  181. buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
  182. for (Object arg : args) {
  183. buf.append(":").append(arg.toString());
  184. }
  185. }
  186. return buf.toString();
  187. }
  188. /**
  189. * 替换占位参数
  190. * @param buf
  191. * @param args
  192. * @param methodParamNames
  193. * @param ap
  194. * @return
  195. */
  196. private static StringBuilder replaceParam(StringBuilder buf, final Object[] args, String[] methodParamNames, String ap) {
  197. StringBuilder builder = new StringBuilder(buf);
  198. String paramValue = "";
  199. for (int i = 0; i < methodParamNames.length; i++) {
  200. if(ap.startsWith(methodParamNames[i])){
  201. final Object arg = args[i];
  202. if (ap.contains(".")) {
  203. paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf('.') + 1)));
  204. } else {
  205. paramValue = String.valueOf(arg);
  206. }
  207. break;
  208. }
  209. }
  210. int start = builder.indexOf("{" + ap);
  211. int end = start + ap.length() + 2;
  212. builder =builder.replace(start, end, paramValue);
  213. return builder;
  214. }
  215. }
  1. package com.huatech.common.util;
  2. public class StringUtil {
  3. /**
  4. * 首字母大写
  5. *
  6. * @param s
  7. * @return
  8. */
  9. public static String firstCharUpperCase(String s) {
  10. StringBuffer sb = new StringBuffer(s.substring(0, 1).toUpperCase());
  11. sb.append(s.substring(1, s.length()));
  12. return sb.toString();
  13. }
  14. }
  1. package com.huatech.common.util;
  2. import java.util.List;
  3. import java.util.Map;
  4. import java.util.Set;
  5. import java.util.concurrent.TimeUnit;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import org.springframework.data.redis.core.BoundHashOperations;
  10. import org.springframework.data.redis.core.RedisTemplate;
  11. import org.springframework.data.redis.core.StringRedisTemplate;
  12. import com.alibaba.fastjson.JSON;
  13. import com.alibaba.fastjson.JSONObject;
  14. import com.huatech.common.support.SpringContextHolder;
  15. /**
  16. * 通用缓存工具类
  17. * @author lh
  18. * @version 3.0
  19. * @since 2016-6-22
  20. *
  21. */
  22. public class CacheUtils {
  23. private static final Logger LOGGER = LoggerFactory.getLogger(CacheUtils.class);
  24. public static RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
  25. public static StringRedisTemplate stringRedisTemplate = SpringContextHolder.getBean("stringRedisTemplate");
  26. private static String redisKeyPrefix = PropertiesUtil.getValueByKey(CacheUtils.class.getResource("/").getPath() + "config/redis.properties", "redis.keyPrefix");
  27. private CacheUtils() {
  28. }
  29. /**
  30. * 删除缓存<br>
  31. * 根据key精确匹配删除
  32. *
  33. * @param key
  34. */
  35. public static void del(String... key) {
  36. LOGGER.warn("delete cache, keys in ({})", merge(key));
  37. for (String k : key) {
  38. redisTemplate.delete(appendKeyPrefix(k));
  39. }
  40. }
  41. /**
  42. * 批量删除<br>
  43. * (该操作会执行模糊查询,请尽量不要使用,以免影响性能或误删)
  44. *
  45. * @param pattern
  46. */
  47. public static void batchDel(String... pattern) {
  48. LOGGER.warn("batchDel cache, pattern in ({})", merge(pattern));
  49. for (String kp : pattern) {
  50. redisTemplate.delete(redisTemplate.keys(appendKeyPrefix(kp) + "*"));
  51. }
  52. }
  53. /**
  54. * 取得缓存(int型)
  55. *
  56. * @param key
  57. * @return
  58. */
  59. public static Integer getInt(String key) {
  60. String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  61. if (StringUtils.isNotBlank(value)) {
  62. return Integer.valueOf(value);
  63. }
  64. return 0;
  65. }
  66. /**
  67. * 取得缓存(long型)
  68. *
  69. * @param key
  70. * @return
  71. */
  72. public static Long getLong(String key) {
  73. String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  74. if (StringUtils.isNotBlank(value)) {
  75. return Long.valueOf(value);
  76. }
  77. return 0l;
  78. }
  79. /**
  80. * 取得缓存(字符串类型)
  81. *
  82. * @param key
  83. * @return
  84. */
  85. public static String getStr(String key) {
  86. return stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  87. }
  88. /**
  89. * 取得缓存(字符串类型)
  90. *
  91. * @param key
  92. * @return
  93. */
  94. public static String getStr(String key, boolean retain) {
  95. String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  96. if (!retain) {
  97. stringRedisTemplate.delete(appendKeyPrefix(key));
  98. }
  99. return value;
  100. }
  101. /**
  102. * 获取缓存<br>
  103. * 注:基本数据类型(Character除外),请直接使用get(String key, Class<T> clazz)取值
  104. *
  105. * @param key
  106. * @return
  107. */
  108. public static Object getObj(String key) {
  109. return redisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  110. }
  111. /**
  112. * 获取缓存<br>
  113. * 注:java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
  114. *
  115. * @param key
  116. * @param retain
  117. *            是否保留
  118. * @return
  119. */
  120. public static Object getObj(String key, boolean retain) {
  121. Object obj = redisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  122. if (!retain && obj != null) {
  123. redisTemplate.delete(appendKeyPrefix(key));
  124. }
  125. return obj;
  126. }
  127. /**
  128. * 获取缓存<br>
  129. * 注:慎用java基本数据类型进行转换(可能会出现空值,转换报错)
  130. *
  131. * @param key
  132. *            key
  133. * @param clazz
  134. *            类型
  135. * @return
  136. */
  137. @SuppressWarnings("unchecked")
  138. public static <T> T get(String key, Class<T> clazz) {
  139. key = appendKeyPrefix(key);
  140. if (clazz.equals(String.class)) {
  141. return (T) stringRedisTemplate.boundValueOps(key).get();
  142. } else if (clazz.equals(Integer.class) || clazz.equals(Long.class)) {
  143. return (T) stringRedisTemplate.boundValueOps(key).get();
  144. } else if (clazz.equals(Double.class) || clazz.equals(Float.class)) {
  145. return (T) stringRedisTemplate.boundValueOps(key).get();
  146. } else if (clazz.equals(Short.class) || clazz.equals(Boolean.class)) {
  147. return (T) stringRedisTemplate.boundValueOps(key).get();
  148. }
  149. return (T) redisTemplate.boundValueOps(key).get();
  150. }
  151. /**
  152. * 将value对象写入缓存
  153. *
  154. * @param key
  155. * @param value
  156. * @param seconds
  157. *            失效时间(秒)
  158. */
  159. public static void set(String key, Object value, long seconds) {
  160. if (null == key || null == value) {
  161. throw new RuntimeException("key or value must not null");
  162. }
  163. key = appendKeyPrefix(key);
  164. if (value instanceof String) {
  165. stringRedisTemplate.opsForValue().set(key, value.toString());
  166. } else if (value instanceof Integer || value instanceof Long) {
  167. stringRedisTemplate.opsForValue().set(key, value.toString());
  168. } else if (value instanceof Double || value instanceof Float) {
  169. stringRedisTemplate.opsForValue().set(key, value.toString());
  170. } else if (value instanceof Short || value instanceof Boolean) {
  171. stringRedisTemplate.opsForValue().set(key, value.toString());
  172. } else {
  173. redisTemplate.opsForValue().set(key, value);
  174. }
  175. if (seconds > 0) {
  176. redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
  177. }
  178. }
  179. /**
  180. * 更新key对象field的值
  181. *
  182. * @param key
  183. *            缓存key
  184. * @param field
  185. *            缓存对象field
  186. * @param value
  187. *            缓存对象field值
  188. */
  189. public static void setJsonField(String key, String field, String value) {
  190. JSONObject obj = JSON.parseObject(stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get());
  191. obj.put(field, value);
  192. stringRedisTemplate.opsForValue().set(appendKeyPrefix(key), obj.toJSONString());
  193. }
  194. /**
  195. * 递减操作
  196. *
  197. * @param key
  198. * @param by
  199. * @return
  200. */
  201. public static double decr(String key, double by) {
  202. return redisTemplate.opsForValue().increment(appendKeyPrefix(key), -by);
  203. }
  204. /**
  205. * 递增操作
  206. *
  207. * @param key
  208. * @param by
  209. * @return
  210. */
  211. public static double incr(String key, double by) {
  212. return redisTemplate.opsForValue().increment(appendKeyPrefix(key), by);
  213. }
  214. /**
  215. * 递减操作
  216. *
  217. * @param key
  218. * @param by
  219. * @return
  220. */
  221. public static long decr(String key, long by) {
  222. return redisTemplate.opsForValue().increment(appendKeyPrefix(key), -by);
  223. }
  224. /**
  225. * 递增操作
  226. *
  227. * @param key
  228. * @param by
  229. * @return
  230. */
  231. public static long incr(String key, long by) {
  232. return redisTemplate.opsForValue().increment(appendKeyPrefix(key), by);
  233. }
  234. /**
  235. * 获取double类型值
  236. *
  237. * @param key
  238. * @return
  239. */
  240. public static double getDouble(String key) {
  241. String value = stringRedisTemplate.boundValueOps(appendKeyPrefix(key)).get();
  242. if (StringUtils.isNotBlank(value)) {
  243. return Double.valueOf(value);
  244. }
  245. return 0d;
  246. }
  247. /**
  248. * 设置double类型值
  249. *
  250. * @param key
  251. * @param value
  252. * @param seconds
  253. *            失效时间(秒)
  254. */
  255. public static void setDouble(String key, double value, long seconds) {
  256. stringRedisTemplate.opsForValue().set(appendKeyPrefix(key), String.valueOf(value));
  257. if (seconds > 0) {
  258. stringRedisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
  259. }
  260. }
  261. /**
  262. * 将map写入缓存
  263. *
  264. * @param key
  265. * @param map
  266. */
  267. public static <T> void setMap(String key, Map<String, T> map) {
  268. redisTemplate.opsForHash().putAll(appendKeyPrefix(key), map);
  269. }
  270. /**
  271. * 向key对应的map中添加缓存对象
  272. *
  273. * @param key
  274. * @param map
  275. */
  276. public static <T> void addMap(String key, Map<String, T> map) {
  277. redisTemplate.opsForHash().putAll(appendKeyPrefix(key), map);
  278. }
  279. /**
  280. * 向key对应的map中添加缓存对象
  281. *
  282. * @param key
  283. *            cache对象key
  284. * @param field
  285. *            map对应的key
  286. * @param value
  287. *            值
  288. */
  289. public static void addMap(String key, String field, String value) {
  290. redisTemplate.opsForHash().put(appendKeyPrefix(key), field, value);
  291. }
  292. /**
  293. * 向key对应的map中添加缓存对象
  294. *
  295. * @param key
  296. *            cache对象key
  297. * @param field
  298. *            map对应的key
  299. * @param obj
  300. *            对象
  301. */
  302. public static <T> void addMap(String key, String field, T obj) {
  303. redisTemplate.opsForHash().put(appendKeyPrefix(key), field, obj);
  304. }
  305. /**
  306. * 获取map缓存
  307. *
  308. * @param key
  309. * @param clazz
  310. * @return
  311. */
  312. public static <T> Map<String, T> mget(String key, Class<T> clazz) {
  313. BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(appendKeyPrefix(key));
  314. return boundHashOperations.entries();
  315. }
  316. /**
  317. * 获取map缓存中的某个对象
  318. *
  319. * @param key
  320. * @param field
  321. * @param clazz
  322. * @return
  323. */
  324. @SuppressWarnings("unchecked")
  325. public static <T> T getMapField(String key, String field, Class<T> clazz) {
  326. return (T) redisTemplate.boundHashOps(appendKeyPrefix(key)).get(field);
  327. }
  328. /**
  329. * 删除map中的某个对象
  330. *
  331. * @author lh
  332. * @date 2016年8月10日
  333. * @param key
  334. *            map对应的key
  335. * @param field
  336. *            map中该对象的key
  337. */
  338. public static void delMapField(String key, String... field) {
  339. redisTemplate.opsForHash().delete(appendKeyPrefix(key), field);
  340. }
  341. /**
  342. * 为哈希表key中的域field的值
  343. *
  344. * @param key
  345. *            键
  346. * @param field
  347. *            map域
  348. * @param value
  349. *            增量值
  350. * @return
  351. */
  352. public static long hincr(String key, String field, long value) {
  353. return redisTemplate.opsForHash().increment(appendKeyPrefix(key), field, value);
  354. }
  355. public static void hset(String key, String field, Object value){
  356. redisTemplate.opsForHash().put(appendKeyPrefix(key), field, value);
  357. }
  358. public static Object hget(String key, String field){
  359. return redisTemplate.boundHashOps(appendKeyPrefix(key)).get(field);
  360. }
  361. public static void hdel(String key, String...fields){
  362. if (fields == null || fields.length == 0) {
  363. redisTemplate.delete(appendKeyPrefix(key));
  364. }else{
  365. redisTemplate.opsForHash().delete(appendKeyPrefix(key), fields);
  366. }
  367. }
  368. public static Long hlen(String key){
  369. return redisTemplate.boundHashOps(appendKeyPrefix(key)).size();
  370. }
  371. public static <T> Set<T> hkeys(String key){
  372. return (Set<T>)redisTemplate.boundHashOps(appendKeyPrefix(key)).keys();
  373. }
  374. public static <T> List<T> hvals(String key){
  375. return (List<T>)redisTemplate.boundHashOps(appendKeyPrefix(key)).values();
  376. }
  377. /**
  378. *
  379. * @param key
  380. * @param value
  381. * @param seconds
  382. * @return
  383. */
  384. public static boolean setnx(String key, String value, long seconds) {
  385. boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(appendKeyPrefix(key), value);
  386. if (seconds > 0) {
  387. redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
  388. }
  389. return flag;
  390. }
  391. /**
  392. * 指定缓存的失效时间
  393. *
  394. * @author FangJun
  395. * @date 2016年8月14日
  396. * @param key
  397. *            缓存KEY
  398. * @param seconds
  399. *            失效时间(秒)
  400. */
  401. public static void expire(String key, long seconds) {
  402. redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
  403. }
  404. /**
  405. * 指定缓存的失效时间
  406. *
  407. * @author FangJun
  408. * @date 2016年8月14日
  409. * @param key
  410. *            缓存KEY
  411. * @param seconds
  412. *            失效时间(秒)
  413. */
  414. public static void expire(String key, int seconds) {
  415. redisTemplate.expire(appendKeyPrefix(key), seconds, TimeUnit.SECONDS);
  416. }
  417. /**
  418. * 添加set
  419. *
  420. * @param key
  421. * @param value
  422. */
  423. public static void sadd(String key, String... value) {
  424. redisTemplate.boundSetOps(appendKeyPrefix(key)).add(value);
  425. }
  426. /**
  427. * 删除set集合中的对象
  428. *
  429. * @param key
  430. * @param value
  431. */
  432. public static void srem(String key, String... value) {
  433. redisTemplate.boundSetOps(appendKeyPrefix(key)).remove(value);
  434. }
  435. /**
  436. * 判断key对应的缓存是否存在
  437. *
  438. * @param key
  439. * @return
  440. */
  441. public static boolean exists(String key) {
  442. return redisTemplate.hasKey(appendKeyPrefix(key));
  443. }
  444. /**
  445. * 模糊查询keys
  446. * @param pattern
  447. * @return
  448. */
  449. public static Set<String> keys(String pattern){
  450. return redisTemplate.keys(appendKeyPrefix(pattern));
  451. }
  452. /**
  453. * 合并数组为字符串
  454. * @param strings
  455. * @return
  456. */
  457. private static String merge(String...strings){
  458. if (strings == null || strings.length == 0) {
  459. return null;
  460. }
  461. StringBuffer sb = new StringBuffer();
  462. int len = strings.length;
  463. for (int i = 0; i < len; i++) {
  464. sb.append(strings[i]);
  465. if(len != i+1){
  466. sb.append(",");
  467. }
  468. }
  469. return sb.toString();
  470. }
  471. private static String appendKeyPrefix(String key){
  472. return redisKeyPrefix.concat(key);
  473. }
  474. }

4、增加AOP支持

  1. package com.huatech.support.redis;
  2. import java.util.Set;
  3. import java.util.concurrent.TimeUnit;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.beans.factory.annotation.Value;
  11. import org.springframework.data.redis.core.RedisTemplate;
  12. import org.springframework.data.redis.core.ValueOperations;
  13. import org.springframework.stereotype.Component;
  14. import org.springframework.util.CollectionUtils;
  15. import com.huatech.common.annotation.CacheEvict;
  16. import com.huatech.common.annotation.Cacheable;
  17. import com.huatech.common.util.AopUtils;
  18. import com.huatech.common.util.CacheUtils;
  19. /**
  20. * 缓存操作切面
  21. * 注意:一个支持缓存的方法,在对象内部被调用是不会触发缓存功能的。
  22. * @author lh
  23. *
  24. */
  25. @Aspect
  26. @Component
  27. public class CacheAspect {
  28. /**
  29. * 缓存清除标识前缀
  30. */
  31. public static final String  KEY_PREFIX_CACHE_EVICT="cacheEvict:interval:";
  32. public static final String REDIS_CACHE_SUPPORT = "1";
  33. private static final Logger LOGGER = LoggerFactory.getLogger(CacheAspect.class);
  34. @SuppressWarnings("rawtypes")
  35. @Autowired
  36. private RedisTemplate redisTemplate;
  37. @Value("${redis.cacheSupport}")
  38. private String redisCacheSupport;
  39. @Value("${redis.keyPrefix}")
  40. private String redisKeyPrefix;
  41. /**
  42. * 启用新的get方法,防止缓存被“击穿”
  43. * <p>
  44. * 击穿 :缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,
  45. *      这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
  46. * 如何解决:业界比较常用的做法,是使用mutex。
  47. *      简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成
  48. *      功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行
  49. *      load db的操作并回设缓存;否则,就重试整个get缓存的方法。
  50. * </p>
  51. * @param key
  52. * @param pjp
  53. * @param cache
  54. * @return
  55. * @throws Throwable
  56. */
  57. private Object get(final String key, final ProceedingJoinPoint pjp, final Cacheable cache) throws Throwable {
  58. @SuppressWarnings("unchecked")
  59. ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
  60. Object value = valueOper.get(key); // 从缓存获取数据
  61. if (value == null) { // 代表缓存值过期
  62. // 设置2min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
  63. String keynx = key.concat(":nx");
  64. if (CacheUtils.setnx(keynx, "1", 5)) { // 代表设置成功
  65. value = pjp.proceed();
  66. if (cache.expire() <= 0) { // 如果没有设置过期时间,则无限期缓存
  67. valueOper.set(key, value);
  68. } else { // 否则设置缓存时间
  69. valueOper.set(key, value, cache.expire(), TimeUnit.SECONDS);
  70. }
  71. CacheUtils.del(keynx);
  72. return value;
  73. } else { // 这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
  74. Thread.sleep(10);
  75. return get(key, pjp, cache); // 重试
  76. }
  77. } else {
  78. return value;
  79. }
  80. }
  81. /**
  82. * 添加缓存
  83. * @param pjp
  84. * @param cache
  85. * @return
  86. * @throws Throwable
  87. */
  88. @Around("@annotation(cache)")
  89. public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache)throws Throwable {
  90. if(REDIS_CACHE_SUPPORT.equals(redisCacheSupport)){
  91. String key = redisKeyPrefix.concat(AopUtils.getCacheKey(pjp, cache.key()));
  92. return get(key, pjp, cache);
  93. }
  94. return pjp.proceed();
  95. }
  96. @Around("@annotation(evict)")
  97. public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict)throws Throwable {
  98. if(REDIS_CACHE_SUPPORT.equals(redisCacheSupport)){
  99. Object value = pjp.proceed();
  100. //清除keys对应的缓存数据
  101. if (evict.keys() != null && evict.keys().length > 0) {
  102. for (String keyname : evict.keys()) {
  103. evictByKeyname(pjp, keyname,evict.interval());
  104. }
  105. }
  106. return value;
  107. }
  108. return pjp.proceed();
  109. }
  110. @SuppressWarnings("unchecked")
  111. private void evictByKeyname(final ProceedingJoinPoint pjp, final String keyname, long interval) {
  112. final String key = redisKeyPrefix.concat(AopUtils.getCacheKey(pjp, keyname));
  113. //操作间隔判断
  114. if (interval != 0) {
  115. final String intervalKey = KEY_PREFIX_CACHE_EVICT + key;
  116. if (CacheUtils.incr(intervalKey, 1) > 1) {
  117. return;
  118. }
  119. CacheUtils.expire(intervalKey, interval);
  120. }
  121. LOGGER.info("cacheEvict, key={}", key);
  122. //使用redisTemplate操作缓存
  123. if (keyname.equals(key)) {// 支持批量删除
  124. Set<String> keys = redisTemplate.keys(key.concat("*"));
  125. if (!CollectionUtils.isEmpty(keys)) {
  126. redisTemplate.delete(keys);
  127. }
  128. } else {
  129. redisTemplate.delete(key);
  130. }
  131. }
  132. }

5、扩展spring-data-redis,简化配置

  1. package com.huatech.support.redis;
  2. import static org.springframework.util.Assert.isTrue;
  3. import static org.springframework.util.Assert.notNull;
  4. import static org.springframework.util.StringUtils.split;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.springframework.data.redis.connection.RedisClusterConfiguration;
  7. import org.springframework.data.redis.connection.RedisNode;
  8. /**
  9. * <p>RedisCluster配置</p>
  10. * <p>用于简化spring-data-redis配置</p>
  11. * @author lh
  12. * @version 3.0
  13. * @since 2017-4-5
  14. *
  15. */
  16. public class RedisClusterConfig extends RedisClusterConfiguration{
  17. public RedisClusterConfig(String nodes, Integer maxRedirects) {
  18. super();
  19. initNodes(nodes);
  20. setMaxRedirects(maxRedirects);
  21. }
  22. private void initNodes(String nodes){
  23. if(StringUtils.isBlank(nodes)){
  24. throw new RuntimeException("nodes can not be empty!");
  25. }
  26. String[] hostAndPorts = nodes.split(",");
  27. for (String hostAndPort : hostAndPorts) {
  28. addClusterNode(readHostAndPortFromString(hostAndPort));
  29. }
  30. }
  31. private RedisNode readHostAndPortFromString(String hostAndPort) {
  32. String[] args = split(hostAndPort, ":");
  33. notNull(args, "HostAndPort need to be seperated by  ':'.");
  34. isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
  35. return new RedisNode(args[0], Integer.valueOf(args[1]).intValue());
  36. }
  37. }
  1. package com.huatech.support.redis;
  2. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
  3. /**
  4. * Jedis Single Connection factory
  5. * @author lh
  6. *
  7. */
  8. public class RedisSingleConnFactory extends JedisConnectionFactory {
  9. public RedisSingleConnFactory(String address,int timeout, String password, redis.clients.jedis.JedisPoolConfig poolConfig) {
  10. String[] hostAndPorts = address.split(":");
  11. setHostName(hostAndPorts[0]);
  12. setPort(Integer.valueOf(hostAndPorts[1]));
  13. setTimeout(timeout);
  14. setPassword(password);
  15. setPoolConfig(poolConfig);
  16. }
  17. public RedisSingleConnFactory(String address, String password, redis.clients.jedis.JedisPoolConfig poolConfig) {
  18. String[] hostAndPorts = address.split(":");
  19. setHostName(hostAndPorts[0]);
  20. setPort(Integer.valueOf(hostAndPorts[1]));
  21. setPassword(password);
  22. setPoolConfig(poolConfig);
  23. }
  24. public RedisSingleConnFactory(String address, redis.clients.jedis.JedisPoolConfig poolConfig) {
  25. String[] hostAndPorts = address.split(":");
  26. setHostName(hostAndPorts[0]);
  27. setPort(Integer.valueOf(hostAndPorts[1]));
  28. setPoolConfig(poolConfig);
  29. }
  30. }

6、添加redis配置文件:redis.properties

  1. #redis settings
  2. redis.cacheSupport=1
  3. redis.keyPrefix=bds_
  4. #redis pool settings
  5. redis.pool.maxTotal=60000
  6. redis.pool.maxIdle=300
  7. redis.pool.testOnBorrow=true
  8. #redis 单机配置
  9. spring.redis.single.node=172.16.90.30:6379
  10. spring.redis.single.timeout=8000
  11. spring.redis.single.password=redis
  12. #redis 集群
  13. spring.redis.cluster.maxRedirects=3
  14. 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配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xmlns:aop="http://www.springframework.org/schema/aop"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  10. http://www.springframework.org/schema/context
  11. http://www.springframework.org/schema/context/spring-context-4.0.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" >
  14. <description>Redis Configuration</description>
  15. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  16. <property name="order" value="1" />
  17. <property name="ignoreUnresolvablePlaceholders" value="true" />
  18. <property name="locations">
  19. <list>
  20. <value>classpath:/mybatis/jdbc.properties</value>
  21. <value>classpath:/config/redis.properties</value>
  22. </list>
  23. </property>
  24. </bean>
  25. <aop:aspectj-autoproxy/>
  26. <context:component-scan base-package="com.huatech.support"/>
  27. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  28. <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数  -->
  29. <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 -->
  30. <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
  31. </bean>
  32. <!-- ======================单机配置 start ====================== -->
  33. <!-- spring-data-redis 单机配置 -->
  34. <bean id="jedisConnFactory" class="com.huatech.support.redis.RedisSingleConnFactory" >
  35. <constructor-arg name="address" value="${spring.redis.single.node}"/>
  36. <constructor-arg name="timeout" type="int" value="${spring.redis.single.timeout}" />
  37. <constructor-arg name="password" value="${spring.redis.single.password}" />
  38. <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
  39. </bean>
  40. <!-- ======================单机配置 end ====================== -->
  41. <!-- ======================cluster配置 start ====================== -->
  42. <!-- spring-data-redis-cluster
  43. <bean id="redisClusterConfiguration"  class="com.huatech.support.redis.RedisClusterConfig" >
  44. <constructor-arg value="${spring.redis.cluster.nodes}"/>
  45. <constructor-arg value="${spring.redis.cluster.maxRedirects}" />
  46. </bean>
  47. <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true">
  48. <constructor-arg ref="redisClusterConfiguration" />
  49. <constructor-arg ref="jedisPoolConfig" />
  50. </bean>
  51. -->
  52. <!-- ======================cluster配置 end ====================== -->
  53. <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
  54. <bean id ="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
  55. <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
  56. p:connectionFactory-ref="jedisConnFactory" />
  57. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
  58. p:connectionFactory-ref="jedisConnFactory"
  59. p:keySerializer-ref="stringRedisSerializer"
  60. p:hashKeySerializer-ref="stringRedisSerializer"
  61. p:valueSerializer-ref="jdkSerializationRedisSerializer" />
  62. </beans>

8、系统中添加缓存支持

  1. @Override
  2. @Cacheable(key="sc:code:{configCode}", expire = 0)
  3. public SystemConfig findByConfigCode(String configCode) {
  4. return systemConfigDao.findByConfigCode(configCode);
  5. }

9、简单测试

  1. import org.junit.Test;
  2. import org.junit.runner.RunWith;
  3. import org.springframework.test.context.ContextConfiguration;
  4. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  5. import com.rd.bds.core.common.util.CacheUtils;
  6. @RunWith(SpringJUnit4ClassRunner.class)
  7. @ContextConfiguration(locations = { "classpath:/spring/spring-config.xml", "classpath:/spring/spring-redis.xml" })
  8. public class CacheUtilsTest {
  9. @Test
  10. public void testString()throws Exception{
  11. String key = "email";
  12. CacheUtils.set(key, "lh@erongdu.com", 20);
  13. System.out.println(key+":"+CacheUtils.getStr(key));
  14. Thread.sleep(1500);
  15. System.out.println(key+":"+CacheUtils.getStr(key));
  16. Thread.sleep(1500);
  17. System.out.println(key+":"+CacheUtils.getStr(key));
  18. }
  19. }
  1. import javax.annotation.Resource;
  2. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.test.context.ContextConfiguration;
  5. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  6. import com.rd.bds.core.service.SystemConfigService;
  7. @RunWith(SpringJUnit4ClassRunner.class)
  8. @ContextConfiguration(locations = { "classpath:/spring/spring-config.xml", "classpath:/spring/spring-redis.xml" })
  9. public class SystemConfigServiceImplTest {
  10. @Resource
  11. private SystemConfigService systemConfigService;
  12. @Test
  13. public void test()throws Exception{
  14. systemConfigService.findByConfigCode("invest_award_limit");
  15. Thread.sleep(100L);
  16. systemConfigService.findByConfigCode("invest_award_limit");
  17. Thread.sleep(100L);
  18. systemConfigService.findByConfigCode("invest_award_limit");
  19. }
  20. }

spring-data-redis分布式的更多相关文章

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

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

  2. spring mvc Spring Data Redis RedisTemplate [转]

    http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...

  3. Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解

    一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...

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

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

  5. Spring data redis的一个bug

    起因 前两天上线了一个新功能,导致线上业务的缓存总是无法更新,报错也是非常奇怪,redis.clients.jedis.exceptions.JedisConnectionException: Unk ...

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

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

  7. spring data redis 理解

    前言 Spring Data Redis project,应用了Spring概念来开发使用键值形式的数据存储的解决方案.我们(官方)提供了一个 "template" ,这是一个高级 ...

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

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

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

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

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

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

随机推荐

  1. Bomb Game HDU - 3622(二分最小值最大化)

    题意: 就是给出n对坐标,每对只能选一个,以选出来的点为圆心,半径自定义,画圆,而这些圆不能覆盖,求半径最小的圆的最大值 解析: 看到最x值最x化,那二分变为判定性问题,然后...然后我就没想到... ...

  2. pfSense用户界面汉化翻译教程

    pfSense用户界面汉化翻译教程 来源 https://blog.51cto.com/fxn2025/2087182 为了记录自己的汉化过程,同时也为了方便网友自己制作汉化版本,我把自己汉化pfSe ...

  3. 【LOJ#6060】Set(线性基)

    [LOJ#6060]Set(线性基) 题面 LOJ 题解 好题啊QwQ. 首先\(x1\oplus x2=s\)是定值.而\(s\)中假设某一位上是\(1\),则\(x1,x2\)上必定有一个是\(1 ...

  4. [luogu4139]上帝与集合的正确用法【欧拉定理+扩展欧拉定理】

    题目大意 让你求\(2^{2^{2^{\cdots}}}(mod)P\)的值. 前置知识 知识1:无限次幂怎么解决 让我们先来看一道全国数学竞赛的一道水题: 让你求解:\(x^{x^{x^{\cdot ...

  5. BZOJ 2839: 集合计数 解题报告

    BZOJ 2839: 集合计数 Description 一个有\(N\)个元素的集合有\(2^N\)个不同子集(包含空集),现在要在这\(2^N\)个集合中取出若干集合(至少一个),使得 它们的交集的 ...

  6. CentOS装个NTP时间同步服务器

    服务端: driftfile /var/lib/ntp/drift restrict default nomodify notrap nopeer noquery restrict 127.0.0.1 ...

  7. linux复制文件到一个不存在的文件夹

    复制文件到一个不存在的文件夹时,会报错 cp -f aaa /home/admin/.m2/cp: 无法创建普通文件"/home/admin/.m2/": 是一个目录 解决的方式: ...

  8. Tutorial: Build a Spring WebMVC App with Primefaces

    Tutorial: Build a Spring WebMVC App with Primefaces by Team Stormpath | September 7, 2016 | Java Pri ...

  9. GNOME禁用GDM中night-light功能

    Night-light feature is enabled also in GDM screen, see here : https://bugzilla.gnome.org/show_bug.cg ...

  10. 百度地图API,展示地图和添加控件

    1.申请百度账号和AK 点我申请 2.准备页面 根据HTML标准,每一份HTML文档都应该声明正确的文档类型,我们建议您使用最新的符合HTML5规范的文档声明: <!DOCTYPE html&g ...