下面是用redis编写自定义注解实现进程锁:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redission.lock.aop.annonation; 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, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface UseRLock { long maxLockTime() default 5000L; String area() default "default"; String clazz() default "default"; String method() default "default"; UseRLock.LockPolicy policy() default UseRLock.LockPolicy.REJECT; public static enum LockPolicy { WAIT, REJECT; private LockPolicy() { } } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redission.lock.aop.annonation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface LockKey { }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redission.lock.aop; public class RLockException extends RuntimeException { public RLockException(String message) { super(message); } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redission.lock.aop; import com.hr360.commons.exception.hr360Exception; import com.hr360.redission.lock.aop.annonation.LockKey; import com.hr360.redission.lock.aop.annonation.UseRLock; import com.hr360.redission.lock.aop.annonation.UseRLock.LockPolicy; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.concurrent.TimeUnit; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.StringUtils; @Aspect @Component public class UseRLockAspect { private static final String REDIS_NS = "rlock:"; private static final String DEFAULT = "default"; private final Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired private RedissonClient redissonClient; public UseRLockAspect() { } @AfterThrowing( value = "@annotation(com.hr360.redission.lock.aop.annonation.UseRLock)", throwing = "e" ) public void afterThrowing(final JoinPoint joinPoint, Exception e) throws Throwable { if (!TransactionSynchronizationManager.isSynchronizationActive()) { this.unlock(joinPoint); } else { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { public void afterCompletion(int status) { try { UseRLockAspect.this.unlock(joinPoint); } catch (NoSuchMethodException var3) { UseRLockAspect.this.log.error("unlock:{}", var3); } } }); } } @AfterReturning("@annotation(com.hr360.redission.lock.aop.annonation.UseRLock)") public void after(final JoinPoint joinPoint) throws Throwable { if (!TransactionSynchronizationManager.isSynchronizationActive()) { this.unlock(joinPoint); } else { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { public void afterCommit() { try { UseRLockAspect.this.unlock(joinPoint); } catch (NoSuchMethodException var2) { UseRLockAspect.this.log.error("unlock:{}", var2); } } }); } } private void unlock(JoinPoint joinPoint) throws NoSuchMethodException { UseRLock annotation = this.getUseRLock(joinPoint); String lockKey = this.getLockKey(annotation.area(), annotation.clazz(), annotation.method(), joinPoint); RLock lock = this.redissonClient.getLock(lockKey); if (lock != null && lock.isHeldByCurrentThread() && lock.isLocked()) { lock.unlock(); } } @Around("@annotation(com.hr360.redission.lock.aop.annonation.UseRLock)") public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable { this.lock(pjp); return pjp.proceed(); } private void lock(ProceedingJoinPoint pjp) throws NoSuchMethodException { UseRLock annotation = this.getUseRLock(pjp); long maxLockTime = annotation.maxLockTime(); String area = annotation.area(); String clazz = annotation.clazz(); String method = annotation.method(); LockPolicy policy = annotation.policy(); RLock lock = null; String lockKey = this.getLockKey(area, clazz, method, pjp); lock = this.redissonClient.getLock(lockKey); boolean locked = lock.isLocked(); if (locked && LockPolicy.REJECT.equals(policy)) { throw new hr360Exception((String)null, "此业务调用仅允许一个并发处理"); } else { lock.lock(maxLockTime, TimeUnit.MILLISECONDS); } } private UseRLock getUseRLock(JoinPoint pjp) throws NoSuchMethodException { Signature signature = pjp.getSignature(); MethodSignature mSignature = (MethodSignature)signature; Class<?> declaringType = mSignature.getDeclaringType(); Method method = declaringType.getDeclaredMethod(mSignature.getName(), mSignature.getParameterTypes()); return (UseRLock)method.getAnnotation(UseRLock.class); } private String getLockKey(String area, String clazz, String methodName, JoinPoint pjp) throws NoSuchMethodException { Signature signature = pjp.getSignature(); MethodSignature mSignature = (MethodSignature)signature; Class<?> declaringType = mSignature.getDeclaringType(); Method method = declaringType.getDeclaredMethod(mSignature.getName(), mSignature.getParameterTypes()); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if (parameterAnnotations != null && parameterAnnotations.length != 0) { if ("default".equals(clazz)) { clazz = declaringType.getName(); } if ("default".equals(methodName)) { methodName = method.getName(); } Parameter[] parameters = method.getParameters(); Object[] args = pjp.getArgs(); StringBuilder lockKey = new StringBuilder(); lockKey.append("rlock:"); lockKey.append(area); lockKey.append(":"); lockKey.append(clazz); lockKey.append(":"); lockKey.append(methodName); lockKey.append(":"); boolean hasLockKey = false; for(int index = 0; index < args.length; ++index) { Parameter parameter = parameters[index]; LockKey annotation = (LockKey)parameter.getAnnotation(LockKey.class); if (annotation != null) { Object arg = args[index]; String argStr = arg.toString(); if (arg == null || StringUtils.isEmpty(argStr)) { throw new RLockException("@LockKey parameter must not be null"); } hasLockKey = true; lockKey.append(argStr); } } if (!hasLockKey) { throw new RLockException("@LockKey must be assigned"); } else { return lockKey.toString(); } } else { throw new RLockException("@LockKey must be assigned"); } } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redisson.autoconfigure; import java.time.Duration; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties( prefix = "hr360.redission.redis" ) public class RedissionProperties { private int database = 0; private String url; private String host = "localhost"; private String password; private int port = 6379; private boolean ssl; private Duration timeout; private RedissionProperties.Sentinel sentinel; private RedissionProperties.Cluster cluster; private final RedissionProperties.Jedis jedis = new RedissionProperties.Jedis(); private final RedissionProperties.Lettuce lettuce = new RedissionProperties.Lettuce(); public RedissionProperties() { } public int getDatabase() { return this.database; } public void setDatabase(int database) { this.database = database; } public String getUrl() { return this.url; } public void setUrl(String url) { this.url = url; } public String getHost() { return this.host; } public void setHost(String host) { this.host = host; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } public int getPort() { return this.port; } public void setPort(int port) { this.port = port; } public boolean isSsl() { return this.ssl; } public void setSsl(boolean ssl) { this.ssl = ssl; } public void setTimeout(Duration timeout) { this.timeout = timeout; } public Duration getTimeout() { return this.timeout; } public RedissionProperties.Sentinel getSentinel() { return this.sentinel; } public void setSentinel(RedissionProperties.Sentinel sentinel) { this.sentinel = sentinel; } public RedissionProperties.Cluster getCluster() { return this.cluster; } public void setCluster(RedissionProperties.Cluster cluster) { this.cluster = cluster; } public RedissionProperties.Jedis getJedis() { return this.jedis; } public RedissionProperties.Lettuce getLettuce() { return this.lettuce; } public static class Pool { private int maxIdle = 8; private int minIdle = 0; private int maxActive = 8; private Duration maxWait = Duration.ofMillis(-1L); public Pool() { } public int getMaxIdle() { return this.maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMinIdle() { return this.minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return this.maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public Duration getMaxWait() { return this.maxWait; } public void setMaxWait(Duration maxWait) { this.maxWait = maxWait; } } public static class Cluster { private List<String> nodes; private Integer maxRedirects; public Cluster() { } public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } public Integer getMaxRedirects() { return this.maxRedirects; } public void setMaxRedirects(Integer maxRedirects) { this.maxRedirects = maxRedirects; } } public static class Sentinel { private String master; private List<String> nodes; public Sentinel() { } public String getMaster() { return this.master; } public void setMaster(String master) { this.master = master; } public List<String> getNodes() { return this.nodes; } public void setNodes(List<String> nodes) { this.nodes = nodes; } } public static class Jedis { private RedissionProperties.Pool pool; public Jedis() { } public RedissionProperties.Pool getPool() { return this.pool; } public void setPool(RedissionProperties.Pool pool) { this.pool = pool; } } public static class Lettuce { private Duration shutdownTimeout = Duration.ofMillis(100L); private RedissionProperties.Pool pool; public Lettuce() { } public Duration getShutdownTimeout() { return this.shutdownTimeout; } public void setShutdownTimeout(Duration shutdownTimeout) { this.shutdownTimeout = shutdownTimeout; } public RedissionProperties.Pool getPool() { return this.pool; } public void setPool(RedissionProperties.Pool pool) { this.pool = pool; } } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.hr360.redisson.autoconfigure; import com.hr360.redisson.autoconfigure.RedissionProperties.Sentinel; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.ClusterServersConfig; import org.redisson.config.Config; import org.redisson.config.SentinelServersConfig; import org.redisson.config.SingleServerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.data.redis.core.RedisOperations; import org.springframework.util.ReflectionUtils; @Configuration @EnableAspectJAutoProxy @ComponentScan( basePackages = {"com.hr360.redission.lock.aop"} ) @ConditionalOnClass({Redisson.class, RedisOperations.class}) @AutoConfigureBefore({RedisAutoConfiguration.class}) @EnableConfigurationProperties({RedissionProperties.class}) public class RedissonAutoConfiguration { private static final Logger log = LoggerFactory.getLogger(RedissonAutoConfiguration.class); @Autowired private RedissionProperties redissionProperties; public RedissonAutoConfiguration() { log.info("#### init RedissionAutoConfiguration success #### 20210306 Great"); } @Bean( destroyMethod = "shutdown" ) @ConditionalOnMissingBean({RedissonClient.class}) public RedissonClient redisson() { Config config = null; Method clusterMethod = ReflectionUtils.findMethod(RedissionProperties.class, "getCluster"); Method timeoutMethod = ReflectionUtils.findMethod(RedissionProperties.class, "getTimeout"); Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, this.redissionProperties); int timeout; Method nodesMethod; if (null == timeoutValue) { timeout = 10000; } else if (!(timeoutValue instanceof Integer)) { nodesMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis"); timeout = ((Long)ReflectionUtils.invokeMethod(nodesMethod, timeoutValue)).intValue(); } else { timeout = (Integer)timeoutValue; } if (this.redissionProperties.getSentinel() != null) { nodesMethod = ReflectionUtils.findMethod(Sentinel.class, "getNodes"); Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, this.redissionProperties.getSentinel()); String[] nodes; if (nodesValue instanceof String) { nodes = this.convert(Arrays.asList(((String)nodesValue).split(","))); } else { nodes = this.convert((List)nodesValue); } config = new Config(); ((SentinelServersConfig)config.useSentinelServers().setMasterName(this.redissionProperties.getSentinel().getMaster()).addSentinelAddress(nodes).setDatabase(this.redissionProperties.getDatabase()).setConnectTimeout(timeout)).setPassword(this.redissionProperties.getPassword()); } else { Method method; if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, this.redissionProperties) != null) { Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, this.redissionProperties); method = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes"); List<String> nodesObject = (List)ReflectionUtils.invokeMethod(method, clusterObject); String[] nodes = this.convert(nodesObject); config = new Config(); ((ClusterServersConfig)config.useClusterServers().addNodeAddress(nodes).setConnectTimeout(timeout)).setPassword(this.redissionProperties.getPassword()); } else { config = new Config(); String prefix = "redis://"; method = ReflectionUtils.findMethod(RedissionProperties.class, "isSsl"); if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, this.redissionProperties)) { prefix = "rediss://"; } ((SingleServerConfig)((SingleServerConfig)config.useSingleServer().setAddress(prefix + this.redissionProperties.getHost() + ":" + this.redissionProperties.getPort()).setConnectTimeout(timeout)).setDatabase(this.redissionProperties.getDatabase()).setPassword(this.redissionProperties.getPassword())).setConnectionMinimumIdleSize(this.redissionProperties.getJedis().getPool().getMinIdle()).setConnectionPoolSize(this.redissionProperties.getJedis().getPool().getMaxActive()); } } return Redisson.create(config); } private String[] convert(List<String> nodesObject) { List<String> nodes = new ArrayList(nodesObject.size()); Iterator var3 = nodesObject.iterator(); while(true) { while(var3.hasNext()) { String node = (String)var3.next(); if (!node.startsWith("redis://") && !node.startsWith("rediss://")) { nodes.add("redis://" + node); } else { nodes.add(node); } } return (String[])nodes.toArray(new String[nodes.size()]); } } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>hr360-redisson-starter</artifactId> <packaging>jar</packaging> <version>2.8.221.fix</version> <properties> <org.redisson.version>3.12.4</org.redisson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>4.0.0</version> </dependency> <!--<dependency>--> <!--<groupId>org.springframework</groupId>--> <!--<artifactId>spring-web</artifactId>--> <!--</dependency>--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.2</version> <scope>compile</scope> </dependency> </dependencies> </project>