项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段if else的代码,导致耦合非常大,不方便后期的修改。
思来想去,决定使用自动注解+Spring AOP来实现。
直接贴代码。
自定义注解类:
package com.ns.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit; /** * * --------- * @author Han */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RedisCached { /** * redis key * @return */ String value(); /** * 过期时间,默认为1分钟,如果要设置永不过期,请设置小于等于0的值 * @return */ long timeout() default 1; /** * 时间单位,默认为分钟 * @return */ TimeUnit timeunit() default TimeUnit.MINUTES; /** * 额外定义一个空方法,调用该方法来对之前的缓存进行更新 * @return */ boolean forDelete() default false; }
这个注解方便我们标志那个方法需要作为AOP的切入点。
AOP实现:
package com.ns.redis.aop; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.ArrayUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.hibernate.metamodel.binding.Caching; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Component; import com.ns.annotation.RedisCached; import com.ns.redis.dao.base.BaseRedisDao; /** * 对dao的getbean的缓存处理 * order保证优先执行此切面 * @author Han */ @Aspect public class AutoRedisCached extends BaseRedisDao<Object, Object> implements Ordered{ private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class); /* * 约束任意包下的包含Dao的类的任意方法,并且被cached注解 */ @Pointcut("execution(* *..*(..)) && @annotation(com.ns.annotation.RedisCached)") private void cacheMethod(){} @Around("cacheMethod()") public Object doArround(ProceedingJoinPoint pjp) throws Throwable{ Object[] args = pjp.getArgs(); MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); Method method = methodSignature.getMethod(); final RedisCached cacheinfo = method.getAnnotation(RedisCached.class); //定义序列化器 final RedisSerializer<String> keySerializer = getStringSerializer(); final RedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(method.getReturnType()); //序列化参数,作为hashkey byte [] keyBytesTemp = keySerializer.serialize(cacheinfo.value()); for(Object arg : args){ keyBytesTemp = ArrayUtils.addAll(keyBytesTemp, getDefaultSerializer().serialize(arg)); } //取md5后key final byte [] keyBytes = keySerializer.serialize(DigestUtils.md5Hex(keyBytesTemp)); //是删除方法 if(cacheinfo.forDelete()){ execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { return connection.del(keyBytes); } }); return null; } Object obj= null; log.info("方法"+method.getName()+"切面,尝试从缓存获取..."); obj = execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte [] tmp = connection.get(keyBytes); return valueSerializer.deserialize(tmp); } }); if(obj == null){ log.info("方法"+method.getName()+"切面,缓存未找到..."); final Object objReturn = pjp.proceed(); if(objReturn != null){ execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { if(cacheinfo.timeout() > 0){ connection.setEx(keyBytes, TimeUnit.SECONDS.convert(cacheinfo.timeout(), cacheinfo.timeunit()), valueSerializer.serialize(objReturn)); }else{ connection.set(keyBytes,valueSerializer.serialize(objReturn)); } return true; } }); } obj = objReturn; }else{ log.info("方法"+method.getName()+"切面,缓存命中..."); } //从dao获取 return obj; } @Override public int getOrder() { return -1; } }
注:Orderd接口是为了保证此代码优先于其他切面执行。
时间: 2024-12-24 19:54:24