本人转自http://hbxflihua.iteye.com/blog/2320584#bc2396403
spring目前在@Cacheable和@CacheEvict等注解上不支持缓存时效设置,只允许通过配置文件设置全局时效。这样就很不方便设定时间。比如系统参数和业务数据的时效是不一样的,这给程序开发造成很大的困扰。不得已,我重写了spring的这两个注解。以下是具体实现。
首先定义@Cacheable和@CacheEvict注解类。
- package com.lh.common.annotation;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- import com.rd.ifaes.common.dict.ExpireTime;
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD})
- public @interface Cacheable {
-
- public String key() default "";
-
- public ExpireTime expire() default ExpireTime.NONE;
-
- }
- package com.lh.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;
- @Target({ ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface CacheEvict {
-
- String key() default "";
-
- }
具体的切面代码(CacheAspect.java)如下:
- package com.lh.common.annotation;
-
- import java.util.List;
- 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.springframework.beans.factory.annotation.Autowired;
- 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.rd.ifaes.common.util.ReflectionUtils;
- import com.rd.ifaes.common.util.StringUtils;
- import com.rd.ifaes.core.core.util.CacheUtils;
-
- @Aspect
- @Component
- public class CacheAspect {
-
- @SuppressWarnings("rawtypes")
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Around("@annotation(cache)")
- public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache) throws Throwable {
-
- String key = getCacheKey(pjp, cache.key());
-
-
- @SuppressWarnings("unchecked")
- ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
- Object value = valueOper.get(key);
- if (value != null) {
- return value;
- }
-
- value = pjp.proceed();
- CacheUtils.set(key, value, cache.expire());
- if (cache.expire().getTime() <= 0) {
- valueOper.set(key, value);
- } else {
- valueOper.set(key, value, cache.expire().getTime(), TimeUnit.SECONDS);
- }
- return value;
- }
-
- @SuppressWarnings("unchecked")
- @Around("@annotation(evict)")
- public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict) throws Throwable {
- Object value = pjp.proceed();
- String key = getCacheKey(pjp, evict.key());
-
-
- if (evict.key().equals(key)) {
- Set<String> keys = redisTemplate.keys(key.concat("*"));
- redisTemplate.delete(keys);
- }else{
- redisTemplate.delete(key);
- }
- return value;
- }
-
-
-
- private String getCacheKey(ProceedingJoinPoint pjp, String key) {
-
- StringBuilder buf = new StringBuilder();
- Object[] args = pjp.getArgs();
-
- if(StringUtils.isNotBlank(key)){
- buf.append(key);
- List<String> annoParamNames = AopUtils.getAnnoParams(key);
- String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
- if(!CollectionUtils.isEmpty(annoParamNames)){
- for (String ap : annoParamNames) {
- String paramValue = "";
- for (int i = 0; i < methodParamNames.length; i++) {
- if(ap.startsWith(methodParamNames[i])){
- Object arg = args[i];
- if (ap.contains(".")) {
- paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf(".") + 1)));
- } else {
- paramValue = String.valueOf(arg);
- }
- }
- }
- int start = buf.indexOf("{" + ap);
- int end = start + ap.length() + 2;
- buf = buf.replace(start, end, paramValue);
- }
- }
-
- }else{
- buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
- for (Object arg : args) {
- buf.append(":").append(arg.toString());
- }
- }
-
- return buf.toString();
- }
- }
里面使用到AopUtils.java和ExpireTime.java两个类,具体代码如下:
- package com.lh.common.annotation;
-
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.Signature;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.asm.*;
-
- 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;
-
- public class AopUtils {
-
-
-
-
- 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 e) {
-
- } catch (ClassNotFoundException e) {
-
- } finally {
- if (resourceAsStream != null) {
- try {
- resourceAsStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- assert 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) {
- @Override
- public void visitLocalVariable(String name, String desc,
- String signature, Label start, Label end, int index) {
- int i = index - 1;
-
-
- if (Modifier.isStatic(m.getModifiers())) {
- i = index;
- }
- if (i >= 0 && i < paramNames.length) {
- paramNames[i] = name;
- }
- super.visitLocalVariable(name, desc, signature, start,
- end, index);
- }
-
- };
- }
- }, 0);
-
- return paramNames;
- }
-
-
-
- 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;
- }
-
-
- public static Method getMethod(ProceedingJoinPoint pjp){
- Signature sig = pjp.getSignature();
- MethodSignature msig = null;
- if (!(sig instanceof MethodSignature)) {
- throw new IllegalArgumentException("该注解只能用于方法");
- }
- msig = (MethodSignature) sig;
- Object target = pjp.getTarget();
- Method currentMethod = null;
- try {
- currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
- } catch (NoSuchMethodException e) {
- } catch (SecurityException e) {
- }
- return currentMethod;
- }
-
- public static List<String> getMatcher(String regex, String source) {
- List<String> list = new ArrayList<String>();
- Pattern pattern = Pattern.compile(regex);
- Matcher matcher = pattern.matcher(source);
- while (matcher.find()) {
- list.add(matcher.group());
- }
- return list;
- }
-
-
- public static List<String> getAnnoParams(String source){
- String regex = "(?<=\\{)(.+?)(?=\\})";
- return getMatcher(regex, source);
- }
-
-
- }
- package com.lh.common.dict;
-
- public enum ExpireTime {
-
-
- NONE(0, "无固定期限")
-
-
- ,ONE_SEC(1, "1秒钟")
-
-
- ,FIVE_SEC(5, "5秒钟")
-
-
- ,TEN_SEC(10, "10秒钟")
-
-
- ,HALF_A_MIN(30, "30秒钟")
-
-
- ,ONE_MIN(60, "1分钟")
-
-
- ,FIVE_MIN(5 * 60, "5分钟")
-
-
- ,TEN_MIN(10 * 60, "10分钟")
-
-
- ,TWENTY_MIN(20 * 60, "20分钟")
-
-
- ,HALF_AN_HOUR(30 * 60, "30分钟")
-
-
- ,ONE_HOUR(60 * 60, "1小时")
-
-
- ,ONE_DAY(24 * 60 * 60, "1天")
-
-
- ,ONE_MON(30 * 24 * 60 * 60, "1个月")
-
-
- ,ONE_YEAR(365 * 24 * 60 * 60, "1年")
-
- ;
-
-
- private final int time;
-
- private final String desc;
-
- ExpireTime(int time, String desc) {
- this.time = time;
- this.desc = desc;
- }
-
-
-
- public int getTime() {
- return time;
- }
-
-
- public String getDesc() {
- return desc;
- }
-
-
- public static ExpireTime match(int time){
- if(NONE.getTime() == time){
- return NONE;
- }else if(ONE_SEC.getTime() == time){
- return ONE_SEC;
- }else if(FIVE_SEC.getTime() == time){
- return FIVE_SEC;
- }else if(TEN_SEC.getTime() == time){
- return TEN_SEC;
- }else if(HALF_A_MIN.getTime() == time){
- return HALF_A_MIN;
- }else if(ONE_MIN.getTime() == time){
- return ONE_MIN;
- }else if(FIVE_MIN.getTime() == time){
- return FIVE_MIN;
- }else if(TEN_MIN.getTime() == time){
- return TEN_MIN;
- }else if(TWENTY_MIN.getTime() == time){
- return TWENTY_MIN;
- }else if(HALF_AN_HOUR.getTime() == time){
- return HALF_AN_HOUR;
- }else if(ONE_HOUR.getTime() == time){
- return ONE_HOUR;
- }else if(ONE_DAY.getTime() == time){
- return ONE_DAY;
- }else if(ONE_MON.getTime() == time){
- return ONE_MON;
- }else if(ONE_YEAR.getTime() == time){
- return ONE_YEAR;
- }
- return HALF_AN_HOUR;
- }
-
- }
配置中的RdRedisCache.java 代码如下:
- package com.lh.common.jedis;
-
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
-
- import net.sf.ehcache.Element;
-
- import org.springframework.cache.Cache;
- import org.springframework.cache.support.SimpleValueWrapper;
- import org.springframework.dao.DataAccessException;
- import org.springframework.data.redis.connection.RedisConnection;
- import org.springframework.data.redis.core.RedisCallback;
- import org.springframework.data.redis.core.RedisTemplate;
-
- public class RdRedisCache implements Cache {
-
- private RedisTemplate<String, Object> redisTemplate;
- private String name;
-
- public RedisTemplate<String, Object> getRedisTemplate() {
- return redisTemplate;
- }
-
- public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
- this.redisTemplate = redisTemplate;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public String getName() {
- return this.name;
- }
-
- @Override
- public Object getNativeCache() {
- return this.redisTemplate;
- }
-
- @Override
- public ValueWrapper get(Object key) {
-
- final String keyf = obj2Str(key);
- Object object = null;
- object = redisTemplate.execute(new RedisCallback<Object>() {
- public Object doInRedis(RedisConnection connection)
- throws DataAccessException {
-
- byte[] key = keyf.getBytes();
- byte[] value = connection.get(key);
- if (value == null) {
- return null;
- }
- return toObject(value);
-
- }
- });
- return (object != null ? new SimpleValueWrapper(object) : null);
- }
-
- @Override
- public void put(Object key, Object value) {
-
- final String keyf = obj2Str(key);
- final Object valuef = value;
- final long liveTime = 86400;
-
- redisTemplate.execute(new RedisCallback<Long>() {
- public Long doInRedis(RedisConnection connection)
- throws DataAccessException {
- byte[] keyb = keyf.getBytes();
- byte[] valueb = toByteArray(valuef);
- connection.set(keyb, valueb);
- if (liveTime > 0) {
- connection.expire(keyb, liveTime);
- }
- return 1L;
- }
- });
- }
-
- public String obj2Str(Object key){
- String keyStr = null;
- if(key instanceof Integer){
- keyStr = ((Integer)key).toString();
- }else if(key instanceof Long){
- keyStr = ((Long)key).toString();
- }else {
- keyStr = (String)key;
- }
- return keyStr;
- }
-
-
- private byte[] toByteArray(Object obj) {
- byte[] bytes = null;
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- try {
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(obj);
- oos.flush();
- bytes = bos.toByteArray();
- oos.close();
- bos.close();
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- return bytes;
- }
-
-
- private Object toObject(byte[] bytes) {
- Object obj = null;
- try {
- ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
- ObjectInputStream ois = new ObjectInputStream(bis);
- obj = ois.readObject();
- ois.close();
- bis.close();
- } catch (IOException ex) {
- ex.printStackTrace();
- } catch (ClassNotFoundException ex) {
- ex.printStackTrace();
- }
- return obj;
- }
-
- @Override
- public void evict(Object key) {
- final String keyf = obj2Str(key);
- redisTemplate.execute(new RedisCallback<Long>() {
- public Long doInRedis(RedisConnection connection)
- throws DataAccessException {
- return connection.del(keyf.getBytes());
- }
- });
- }
-
- @Override
- public void clear() {
- redisTemplate.execute(new RedisCallback<String>() {
- public String doInRedis(RedisConnection connection)
- throws DataAccessException {
- connection.flushDb();
- return "ok";
- }
- });
- }
-
- @Override
- public <T> T get(Object key, Class<T> type) {
- ValueWrapper wrapper = get(key);
- return wrapper == null ? null : (T) wrapper.get();
- }
-
- @Override
- public ValueWrapper putIfAbsent(Object key, Object value) {
- synchronized (key) {
- ValueWrapper wrapper = get(key);
- if (wrapper != null) {
- return wrapper;
- }
- put(key, value);
- return toWrapper(new Element(key, value));
- }
- }
-
- private ValueWrapper toWrapper(Element element) {
- return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
- }
-
- }
spring配置文件的相关配置如下:
- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
- <property name="maxIdle" value="${redis.pool.maxIdle}" />
- <property name="maxTotal" value="${redis.pool.maxTotal}" />
- <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
- </bean>
-
- <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
- <constructor-arg index="0" ref="jedisPoolConfig" />
- <constructor-arg index="1" value="${redis.host}" type="String" />
- <constructor-arg index="2" value="${redis.port}" type="int" />
- </bean>
-
-
- <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
- <property name="hostName" value="${redis.host}" />
- <property name="port" value="${redis.port1}" />
- <property name="timeout" value="${redis.timeout}" />
- <property name="poolConfig" ref="jedisPoolConfig" />
- </bean>
-
- <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
-
- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
- p:connectionFactory-ref="jedisConnFactory" p:keySerializer-ref="stringRedisSerializer" />
-
- <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
- <property name="caches">
- <set>
- <bean class="com.lh.common.jedis.RdRedisCache" p:redis-template-ref="redisTemplate" p:name="sysCache"/>
- </set>
- </property>
- </bean>
-
- <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" />
spring-data-redis时效设置
原文:https://www.cnblogs.com/zeussbook/p/9296884.html