基於註解的方式使用spring-integration-redis分散式鎖
摘要:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheLock {
String prefix() default ...
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface CacheLock { String prefix() default ""; //int expire() default 5; //TimeUnit timeUnit() default TimeUnit.SECONDS; String delimiter() default ":"; } 複製程式碼
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface CacheParam { String name() default ""; int order() default -1; } 複製程式碼
二.定義切面類
package com.xxx.xx.common.aop; import com.alibaba.excel.util.StringUtils; import com.xxx.xx.common.annotation.CacheBody; import com.xxx.xx.common.annotation.CacheLock; import com.xxx.xx.common.annotation.CacheParam; import com.xxx.xx.common.aop.dto.CacheParamDTO; import com.xxx.xx.common.dto.Ret; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.concurrent.locks.Lock; import org.apache.commons.lang3.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.CodeSignature; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.redis.util.RedisLockRegistry; import org.springframework.stereotype.Component; /** * @Author: hlm * @Date: 2019/4/4 10:46 */ @Aspect @Component public class CacheLockAop { @Autowired private RedisLockRegistry redisLockRegistry; @Pointcut("@annotation(cacheLock)") public void cacheLockPointCut(CacheLock cacheLock) { } @Around("cacheLockPointCut(cacheLock)") public Object around(ProceedingJoinPoint joinPoint, CacheLock cacheLock) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); if (StringUtils.isEmpty(cacheLock.prefix())) { throw new RuntimeException("key不能為空, 請在@CacheLock中定義prefix=%s"); } CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); String[] parameterNames = codeSignature.getParameterNames(); //獲取方法入參,組裝key List<CacheParamDTO> cacheParamDTOList = new ArrayList<>(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (Annotation[] parameterAnnotation : parameterAnnotations) { int paramIndex = ArrayUtils.indexOf(parameterAnnotations, parameterAnnotation); for (Annotation annotation : parameterAnnotation) { if (annotation instanceof CacheParam) { CacheParamDTO cacheParamDTO = new CacheParamDTO(); if (StringUtils.isEmpty(((CacheParam) annotation).name())) { cacheParamDTO.setName(parameterNames[paramIndex]); } else { cacheParamDTO.setName(((CacheParam) annotation).name()); } cacheParamDTO.setOrder(((CacheParam) annotation).order()); cacheParamDTO.setValue(joinPoint.getArgs()[paramIndex].toString()); cacheParamDTOList.add(cacheParamDTO); } if (annotation instanceof CacheBody) { Object obj = joinPoint.getArgs()[paramIndex]; Class bodyClass = obj.getClass(); Field[] fields = bodyClass.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(CacheParam.class)) { Annotation fieldAnnotation = field.getAnnotation(CacheParam.class); field.setAccessible(true); CacheParamDTO cacheParamDTO = new CacheParamDTO(); if (StringUtils.isEmpty(((CacheParam) fieldAnnotation).name())) { cacheParamDTO.setName(field.getName()); } else { cacheParamDTO.setName(((CacheParam) fieldAnnotation).name()); } cacheParamDTO.setOrder(((CacheParam) fieldAnnotation).order()); cacheParamDTO.setValue((String) field.get(obj)); cacheParamDTOList.add(cacheParamDTO); } } } } } StringBuilder builder = new StringBuilder(); cacheParamDTOList.sort(Comparator.comparing(item -> item.getOrder())); for (CacheParamDTO cacheParamDTO : cacheParamDTOList) { builder.append(cacheLock.delimiter()) .append(cacheParamDTO.getName()) .append(cacheLock.delimiter()) .append(cacheParamDTO.getValue()); } final String key = cacheLock.prefix() + builder.toString(); //spring-integration對redis分佈鎖的支援,底層應該也是lua指令碼的實現,可完美解決執行緒掛掉造成的死鎖,以及執行時間過長鎖釋放掉,誤刪別人的鎖 Lock lock = redisLockRegistry.obtain(key); Boolean lockFlag = lock.tryLock(); if (!lockFlag) { return new Ret<>("M4000", "系統繁忙,請重試"); } Object object = null; try { object = joinPoint.proceed(); } finally { lock.unlock(); } return object; } } 複製程式碼
三.配置檔案
package com.xxx.xx.common.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.integration.redis.util.RedisLockRegistry; /** * @Author: hlm * @Date: 2019/4/8 8:59 */ @Configuration public class RedisLockConfiguration { @Bean public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { return new RedisLockRegistry(redisConnectionFactory, "locks"); } } 複製程式碼
四.maven依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.0.4.RELEASE</version> </dependency> 複製程式碼
本人第一次發帖,望大佬給出意見,在下會十分感謝的