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;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheLock {
/**
* redis 锁key的前缀
*
* @return redis 锁key的前缀
*/
String prefix() default "";
/**
* 过期秒数,默认为10秒
*
* @return 轮询锁的时间
*/
int expire() default 10;
/**
* 超时时间单位
*
* @return 秒
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* <p>Key的分隔符(默认 :)</p>
* <p>生成的Key:N:SO1008:500</p>
*
* @return String
*/
String delimiter() default "_";
}
import com.i78dk.appserver.common.annotation.CacheLock;
import com.i78dk.appserver.common.annotation.CacheParam;
import com.i78dk.appserver.common.constants.BusiConstants;
import com.i78dk.appserver.common.context.UserContext;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class LockKeyGeneratorUtil {
public static String getLockKey(ProceedingJoinPoint pjp) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
CacheLock lockAnnotation = method.getAnnotation(CacheLock.class);
final Object[] args = pjp.getArgs();
final Parameter[] parameters = method.getParameters();
StringBuilder builder = new StringBuilder();
StringBuilder prefix = new StringBuilder(BusiConstants.PREVENT_REPEAT_PREFIX+lockAnnotation.prefix());
//获取当前线程缓存的手机号
Object mobile = UserContext.get().getMobile();
//未指定前缀使用方法名作为前缀
if(BusiConstants.PREVENT_REPEAT_PREFIX.equals(prefix.toString())){
prefix.append(method.getName())
.append(lockAnnotation.delimiter());
}
if(mobile != null){
prefix.append(mobile.toString());
}
//默认解析方法里面带 CacheParam 注解的属性,如果没有尝试着解析实体对象中的
for (int i = 0; i < parameters.length; i++) {
final CacheParam annotation = parameters[i].getAnnotation(CacheParam.class);
if (annotation == null) {
continue;
}
builder.append(lockAnnotation.delimiter()).append(args[i]);
}
if (StringUtils.isEmpty(builder.toString())) {
final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
final Object object = args[i];
final Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
final CacheParam annotation = field.getAnnotation(CacheParam.class);
if (annotation == null) {
continue;
}
field.setAccessible(true);
builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object));
}
}
}
return prefix.toString() + builder.toString();
}
}
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheParam {
/**
* 字段名称
*
* @return String
*/
String name() default "";
}
import com.i78dk.appserver.common.annotation.CacheLock;
import com.i78dk.appserver.common.constants.ResultCodeConstants;
import com.i78dk.appserver.common.exception.AssertException;
import com.i78dk.appserver.common.redis.CacheRedisService;
import com.i78dk.appserver.common.util.LockKeyGeneratorUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect
@Component
public class ServiceLogAspect {
@Autowired
private CacheRedisService cacheRedisService;
private Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
private ThreadLocal<Long> startTime = new ThreadLocal<Long>();
@Pointcut("execution(public * com.i78dk.*.service..*.*(..))")
public void serviceLog() {}
@Before("serviceLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 请求内容
logger.info("[controller]----CLASS_METHOD: " + joinPoint.getSignature().getDeclaringTypeName() + "."
+ joinPoint.getSignature().getName() + ",参数 : "
+ Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "serviceLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 返回内容
logger.info("[controller]----返回值 : " + ret);
}
@Around("serviceLog()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
startTime.set(System.currentTimeMillis());
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
CacheLock key = method.getAnnotation(CacheLock.class);
String lockKey = "lock key is empty";
//防重复提交
if (key != null) {
lockKey = LockKeyGeneratorUtil.getLockKey(pjp);
boolean b = cacheRedisService.setIfAbsent(lockKey, true, key.expire());
if (!b) {
throw new AssertException(ResultCodeConstants.CODE_S0025);
}
}
try {
// ob 为方法的返回值
Object ob = pjp.proceed();
logger.info("[controller]----耗时 : " + (System.currentTimeMillis() - startTime.get()));
return ob;
}finally {
//无论业务是否成功释放锁
if(key != null){
cacheRedisService.remove(lockKey);
}
}
}
}
/**
* 设置有效时间
* @param key
* @param expire
* @return
*/
@Override
public boolean expire(final String key, long expire) {
return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
@Override
public boolean setIfAbsent(String key, Object o, long expire) {
boolean sucess = false;
try {
sucess = redisTemplate.opsForValue().setIfAbsent(key, o);
//保存成功设置过期时间
if(sucess){
expire(key, expire);
}
}catch (Exception e){
e.printStackTrace();
return sucess;
}
return sucess;
}
文章中若有不足之处,还望指出。坚持是一种精神,分享是一种快乐!