自定义注解+Spel实现分布式锁方式

目录
  • 自定义注解+Spel实现分布式锁
    • 依赖
    • RedisLockRegistryConfig
    • 自定义注解
    • 自定义切面
    • 测试类
    • 执行结果
  • 基于注解的方式实现分布式锁
    • redis分布式锁的实现
    • 测试

自定义注解+Spel实现分布式锁

依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-redis</artifactId>
            <version>5.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

RedisLockRegistryConfig

package com.example.demo.config;
import org.springframework.beans.factory.annotation.Value;
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;
@Configuration
public class RedisLockRegistryConfig {
	/**
	 * 默认过期时间300s
	 */
    @Value("${distribute.lock.expireTime:300}")
    private long expireTime;
    @Value("${spring.application.name:'distributeLock'}")
    private String registryKey;
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory factory){
        return new RedisLockRegistry(factory, registryKey, expireTime * 1000);
    }
}

自定义注解

package com.example.demo.aop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
    String name() default "";
}

自定义切面

package com.example.demo.aop;
import com.example.demo.aop.annotation.DistributeLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
@Aspect
@Order
@Component
public class DistributeLockAop {
    private static final Logger LOGGER = LoggerFactory.getLogger(DistributeLockAop.class);
    private static SpelExpressionParser parser = new SpelExpressionParser();
    private static DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    private RedisLockRegistry redisLockRegistry;
    public DistributeLockAop(RedisLockRegistry redisLockRegistry) {
        this.redisLockRegistry = redisLockRegistry;
    }
    @Around("@annotation(com.example.demo.aop.annotation.DistributeLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Class<?> clazz = joinPoint.getTarget().getClass();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = clazz.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        DistributeLock distributeLock = AnnotationUtils.findAnnotation(method, DistributeLock.class);
        assert distributeLock != null;
        String spel = distributeLock.name();
        String lockName = generateKeyBySpEL(spel, method, joinPoint.getArgs());
        Lock lock = redisLockRegistry.obtain(lockName);
        if (lock.tryLock()) {
            LOGGER.info("DistributeLock locked Success. key:{}", lockName);
            return joinPoint.proceed();
        } else {
            LOGGER.error("DistributeLock locked Failure. key:{}", lockName);
            throw new Exception("Lock failure");
        }
    }
    public static String generateKeyBySpEL(String spELString, Method method, Object[] args) {
        String[] paramNames = discoverer.getParameterNames(method);
        Expression expression = parser.parseExpression(spELString);
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            assert paramNames != null;
            context.setVariable(paramNames[i], args[i]);
        }
        return Objects.requireNonNull(expression.getValue(context)).toString();
    }
}

测试类

package com.example.demo.base;
import com.example.demo.aop.annotation.DistributeLock;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
    @DistributeLock(name = "'lock:' + #something.name")
    public void doSomething(Something something) {
    }
}
package com.example.demo.base;
public class Something {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
package com.example.demo;
import com.example.demo.base.Something;
import com.example.demo.base.SomeService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        SomeService someService = context.getBean("someService", SomeService.class);
        Something something = new Something();
        something.setName("gogogo");
        someService.doSomething(something);
    }
}

执行结果

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.4)

2021-03-25 16:27:49.638  INFO 10492 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_141 on P80320948 with PID 10492 (D:\workspace\demo\target\classes started by 80320948 in D:\workspace\demo)
2021-03-25 16:27:49.641  INFO 10492 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-03-25 16:27:50.006  INFO 10492 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-03-25 16:27:50.008  INFO 10492 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-03-25 16:27:50.028  INFO 10492 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 6 ms. Found 0 Redis repository interfaces.
2021-03-25 16:27:50.144  INFO 10492 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2021-03-25 16:27:50.153  INFO 10492 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'taskScheduler' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.
2021-03-25 16:27:50.156  INFO 10492 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2021-03-25 16:27:50.253  INFO 10492 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-25 16:27:50.329  INFO 10492 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-25 16:27:50.330  INFO 10492 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-25 16:27:50.832  INFO 10492 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2021-03-25 16:27:50.872  INFO 10492 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2021-03-25 16:27:50.872  INFO 10492 --- [           main] o.s.i.channel.PublishSubscribeChannel    : Channel 'demo.errorChannel' has 1 subscriber(s).
2021-03-25 16:27:50.872  INFO 10492 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean '_org.springframework.integration.errorLogger'
2021-03-25 16:27:50.878  INFO 10492 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.756 seconds (JVM running for 2.569)
2021-03-25 16:27:51.545  INFO 10492 --- [           main] com.example.demo.aop.DistributeLockAop   : DistributeLock locked Success. key:lock:gogogo

基于注解的方式实现分布式锁

分布式锁的实现有两种方法

  • 基于redis
  • 基于zookeeper

为了方便分布式锁的使用, 基于注解的方式抽取成公用组件

DisLock注解

/**
 * 分布式锁的注解, 通过指定key作为分布式锁的key
 *
 * @author wang.js on 2019/1/29.
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DisLock {
	/**
	 * 分布式锁的key
	 *
	 * @return
	 */
	String key();
	/**
	 * 分布式锁用的业务场景id
	 *
	 * @return
	 */
	String biz();
	/**
	 * 过期时间, 默认是5秒
	 * 单位是秒
	 *
	 * @return
	 */
	int expireTime() default 5;
}

处理DisLock的切面

/**
 * 处理@DisLock注解的切面
 *
 * @author wang.js on 2019/1/29.
 * @version 1.0
 */
@Aspect
@Order(value = 1)
@Component
public class DisLockAspect {
	@Resource
	private DisLockUtil disLockUtil;
	private static final int MIN_EXPIRE_TIME = 3;
	@Around(value = "@annotation(disLock)")
	public Object execute(ProceedingJoinPoint proceedingJoinPoint, DisLock disLock) throws Throwable {
		int expireTIme = disLock.expireTime() < MIN_EXPIRE_TIME ? MIN_EXPIRE_TIME : disLock.expireTime();
		String disKey = CacheKeyParser.parse(proceedingJoinPoint, disLock.key(), disLock.biz());
		boolean lock = disLockUtil.lock(disKey, expireTIme);
		int count = 1;
		while (!lock && count < MIN_EXPIRE_TIME) {
			lock = disLockUtil.lock(disKey, expireTIme);
			count++;
			TimeUnit.SECONDS.sleep(1);
		}
		Object proceed;
		if (lock) {
			// 允许查询
			try {
				proceed = proceedingJoinPoint.proceed();
			} finally {
				// 删除分布式锁
				disLockUtil.unlock(disKey, false);
			}
		} else {
			throw new CustomException(ErrorCodeEnum.DUPLICATE_REQUEST.getMessage());
		}
		return proceed;
	}
}

redis的配置

/**
 * @author wang.js
 * @date 2018/12/17
 * @copyright yougou.com
 */
@Configuration
public class RedisConfig {
	@Value("${spring.redis.host}")
	private String host;
	@Value("${spring.redis.port:6379}")
	private Integer port;
	@Bean
	public JedisPool jedisPool() {
		//1.设置连接池的配置对象
		JedisPoolConfig config = new JedisPoolConfig();
		//设置池中最大连接数
		config.setMaxTotal(50);
		//设置空闲时池中保有的最大连接数
		config.setMaxIdle(10);
		config.setMaxWaitMillis(3000L);
		config.setTestOnBorrow(true);
		//2.设置连接池对象
		return new JedisPool(config,host,port);
	}
}

redis分布式锁的实现

/**
 * redis分布式锁的实现
 *
 * @author wang.js
 * @date 2018/12/18
 * @copyright yougou.com
 */
@Component
public class DisLockUtil {
	@Resource
	private JedisPool jedisPool;
	private static final int DEFAULT_EXPIRE_TIME = 5;
	private static final Long RELEASE_SUCCESS = 1L;
	private static final String LOCK_SUCCESS = "OK";
	private static final String SET_IF_NOT_EXIST = "NX";
	private static final String SET_WITH_EXPIRE_TIME = "PX";
	/**
	 * 尝试获取分布式锁
	 *
	 * @param jedis      Redis客户端
	 * @param lockKey    锁
	 * @param requestId  请求标识
	 * @param expireTime 超期时间
	 * @return 是否获取成功
	 */
	public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
		String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
		if (LOCK_SUCCESS.equals(result)) {
			return true;
		}
		return false;
	}
	/**
	 * 释放分布式锁
	 *
	 * @param jedis     Redis客户端
	 * @param lockKey   锁
	 * @param requestId 请求标识
	 * @return 是否释放成功
	 */
	public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
		if (RELEASE_SUCCESS.equals(result)) {
			return true;
		}
		return false;
	}
	/**
	 * 释放锁
	 *
	 * @param key
	 * @return
	 */
	public final boolean unlock(String key, boolean needCheck) {
		boolean result = false;
		Jedis jedis = jedisPool.getResource();
		try {
			if (needCheck) {
				String expireTimeCache = jedis.get(key);
				// 判断锁是否过期了
				if (StringUtils.isBlank(expireTimeCache)) {
					result = true;
				}
				if (System.currentTimeMillis() - Long.parseLong(expireTimeCache) > 0) {
					// 直接删除
					jedis.del(key);
					result = true;
				}
			} else {
				jedis.del(key);
			}
		} finally {
			jedis.close();
		}
		return result;
	}
	/**
	 * 获取分布式锁
	 *
	 * @param key
	 * @param expireSecond
	 * @return
	 */
	public final boolean lock(String key, int expireSecond) {
		if (StringUtils.isBlank(key)) {
			throw new RuntimeException("传入的key为空");
		}
		expireSecond = expireSecond == 0 ? DEFAULT_EXPIRE_TIME : expireSecond;
		// 过期的时候的时间戳
		long expireTime = System.currentTimeMillis() + expireSecond * 1000 + 1;
		boolean setResult = false;
		Jedis jedis = jedisPool.getResource();
		try {
			if (jedis.setnx(key, String.valueOf(expireTime)) == 1) {
				// 说明加锁成功
				setResult = true;
			}
			if (jedis.ttl(key) < 0) {
				jedis.expire(key, expireSecond);
			}
			if (setResult) {
				return true;
			}
			String expireTimeCache = jedis.get(key);
			System.out.println(expireTimeCache + "====" + jedis.ttl(key) + ", now:" + System.currentTimeMillis());
			// 判断锁是否过期了
			if (StringUtils.isNotBlank(expireTimeCache) && System.currentTimeMillis() - Long.parseLong(expireTimeCache) > 0) {
				String oldExpireTime = jedis.getSet(key, String.valueOf(expireTime));
				if (StringUtils.isNotBlank(oldExpireTime) && oldExpireTime.equals(String.valueOf(expireTime))) {
					jedis.expire(key, expireSecond);
					setResult = true;
				}
			}
		} finally {
			jedis.close();
		}
		return setResult;
	}
}

实现分布式锁的关键是对key的设置, 需要获取实际的参数来设置分布式锁, 这里自定义了解析器

/**
 * cache key 的解析器
 *
 * @author wang.js on 2019/2/27.
 * @version 1.0
 */
public class CacheKeyParser {
	/**
	 * 解析缓存的key
	 *
	 * @param proceedingJoinPoint 切面
	 * @param cacheKey 缓存的key
	 * @param biz 业务
	 * @return String
	 * @throws IllegalAccessException 异常
	 */
	public static String parse(ProceedingJoinPoint proceedingJoinPoint, String cacheKey, String biz) throws IllegalAccessException {
		// 解析实际参数的key
		String key = cacheKey.replace("#", "");
		StringTokenizer stringTokenizer = new StringTokenizer(key, ".");
		Map<String, Object> nameAndValue = getNameAndValue(proceedingJoinPoint);
		Object actualKey = null;
		while (stringTokenizer.hasMoreTokens()) {
			if (actualKey == null) {
				actualKey = nameAndValue.get(stringTokenizer.nextToken());
			} else {
				actualKey = getPropValue(actualKey, stringTokenizer.nextToken());
			}
		}
		return biz + actualKey;
	}
	/**
	 * 获取参数Map集合
	 *
	 * @param joinPoint 切面
	 * @return Map<String, Object>
	 */
	private static Map<String, Object> getNameAndValue(ProceedingJoinPoint joinPoint) {
		Object[] paramValues = joinPoint.getArgs();
		String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
		Map<String, Object> param = new HashMap<>(paramNames.length);
		for (int i = 0; i < paramNames.length; i++) {
			param.put(paramNames[i], paramValues[i]);
		}
		return param;
	}
	/**
	 * 获取指定参数名的参数值
	 *
	 * @param obj
	 * @param propName
	 * @return
	 * @throws IllegalAccessException
	 */
	public static Object getPropValue(Object obj, String propName) throws IllegalAccessException {
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field f : fields) {
			if (f.getName().equals(propName)) {
				//在反射时能访问私有变量
				f.setAccessible(true);
				return f.get(obj);
			}
		}
		return null;
	}
}

ErrorCodeEnum

public enum ErrorCodeEnum {
	SUCCESS("查询成功", "200"),
	SERVER_ERROR("服务器异常", "500"),
	SECKILL_END("秒杀活动已结束", "250"),
	GOODS_KILLED("秒杀成功", "502"),
	ERROR_SIGN("签名不合法", "260"),
	UPDATE_SUCCESS("更新成功", "0"),
	SAVE_SUCCESS("保存成功", "0"),
	UPDATE_FAIL("更新失败", "256"),
	EMPTY_PARAM("参数为空", "257"),
	SAVE_ERROR("保存失败", "262"),
	SERVER_TIMEOUT("调用超时", "501"),
	USER_NOT_FOUND("找不到用户", "502"),
	COUPON_NOT_FOUND("找不到优惠券", "503"),
	DUPLICATE("出现重复", "504"),
	USER_STATUS_ABNORMAL("用户状态异常", "505"),
	NO_TOKEN("无token,请重新登录", "506"),
	ERROR_TOKEN("token不合法", "507"),
	EMPTY_RESULT("暂无数据", "508"),
	DUPLICATE_REQUEST("重复请求", "509"),
	;
	/**
	 * 定义的message
	 */
	private String message;
	/**
	 * 定义的错误码
	 */
	private String errCode;
	ErrorCodeEnum(String message, String errCode) {
		this.message = message;
		this.errCode = errCode;
	}
	public String getMessage() {
		return message;
	}
	protected void setMessage(String message) {
		this.message = message;
	}
	public String getErrCode() {
		return errCode;
	}
	protected void setErrCode(String errCode) {
		this.errCode = errCode;
	}
}

自定义异常CustomException

/**
 * @author Eric on 2018/12/24.
 * @version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class CustomException extends RuntimeException {
    private String message;
}

配置文件

spring:
  redis:
    host: mini7
    port: 6379

测试

定义一个方法, 加上@RedisCache注解, cacheKey的值必须是#实际参数名.属性名的格式, 如果想要成其他的格式可以修改CacheKeyParser中的parse方法

@DisLock(key = "#id", biz = CommonBizConstant.SECOND_KILL)
@Override
public String testRedisCache(String id) {
    LOGGER.info("调用方法获取值");
    return "大傻逼";
}

在springboot启动类上加上@ComponentScan({“com.eric”})

/**
 * @author Eric on 2019/1/26.
 * @version 1.0
 */
@SpringBootApplication
@MapperScan("com.eric.base.data.dao")
@ComponentScan({"com.eric"})
@EnableFeignClients
@EnableDiscoveryClient
public class BaseDataApplication {
    public static void main(String[] args) {
        SpringApplication.run(BaseDataApplication.class, args);
    }
}

写个测试类调用上面的方法

/**
 * 基础数据
 *
 * @author wang.js on 2019/2/27.
 * @version 1.0
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class BaseDataTest {
    @Resource
    private SysDictService sysDictService;
    @Test
    public void t1() {
        for (int i = 0; i < 100; i++) {
            sysDictService.testRedisCache("1");
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java分布式锁的三种实现方案

    方案一:数据库乐观锁 乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的left_count减1,在并发的情况下如何要保证left_count不为负数,乐观锁的实现方式为在红包表上添加一个版本号字段(version),默认为0. 异常实现流程 -- 可能会发生的异常情况 -- 线程1查询,当前left_count为1,则有记录 select * from t_bonus

  • Java注解如何基于Redission实现分布式锁

    这篇文章主要介绍了Java注解如何基于Redission实现分布式锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.定义注解类 @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DistributedLock { //锁名称 String lockName() default ""; /

  • Java基于注解实现的锁实例解析

    背景 某些场景下,有可能一个方法不能被并发执行,有可能一个方法的特定参数不能被并发执行.比如不能将一个消息发送多次,创建缓存最好只创建一次等等.为了实现上面的目标我们就需要采用同步机制来完成,但同步的逻辑如何实现呢,是否会影响到原有逻辑呢? 嵌入式 这里讲的嵌入式是说获取锁以及释放锁的逻辑与业务代码耦合在一起,又分分布式与单机两种不同场景的不同实现. 单机版本 下面方法,每个productId不允许并发访问,所以这里可以直接用synchronized来锁定不同的参数. @Service publ

  • 自定义注解+Spel实现分布式锁方式

    目录 自定义注解+Spel实现分布式锁 依赖 RedisLockRegistryConfig 自定义注解 自定义切面 测试类 执行结果 基于注解的方式实现分布式锁 redis分布式锁的实现 测试 自定义注解+Spel实现分布式锁 依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs

  • 使用自定义注解实现redisson分布式锁

    目录 自定义注解实现redisson分布式锁 自定义注解 aop解析注解 service中使用注解加锁使用 redisson分布式锁应用 应用场景 Redisson管理类 分布式锁 测试类 自定义注解实现redisson分布式锁 自定义注解 package com.example.demo.annotation; import java.lang.annotation.*; /** * desc: 自定义 redisson 分布式锁注解 * * @author: 邢阳 * @mail: xyde

  • Redis分布式锁实现方式及超时问题解决

    一 前言 redis在分布式应用十分广泛,本篇文章也是互联网面试的重点内容,读者至少需要知道为什么需要分布式锁,分布式锁的实现原理,分布式锁的应用场景,在使用分布式锁时遇到哪些问题?你是如何解决的,如果读者能掌握以上问题,那么关于这道面试题,你也就基本过关了: 二 分布式锁的产生背景 分布式锁对应的是多个应用,每个应用中都可能会处理相同的数据,如果多个应用对用一个操作进行了重复操作,就会出现数据不一致,数据重复问题,于是分布式锁应用而生,通常你可以理解为多线程中的synchronized 三 分

  • Java自定义注解用法实例小结

    本文实例讲述了Java自定义注解用法.分享给大家供大家参考,具体如下: 一 自定义注解语法 [public] @interface Annotation的名称 { [数据类型 变量名称();] } 要自定义注解,需要使用@interface方式进行定义,在定义注解时也可以定义各种变量,但是变量之后必须使用括号(). 提示:使用@interface就相对于继承了Annotation接口.在程序中使用@interface声明Annotation,那么此Annotation实际相对于继承了Annota

  • Spring Boot如何通过自定义注解实现日志打印详解

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • SpringBoot通过自定义注解实现日志打印的示例代码

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • 如何使用注解方式实现 Redis 分布式锁

    目录 引入 Redisson 初始化 Redisson 编写 Redisson 分布式锁工具类 声明注解 @Lock 注解解析类 引入 Redisson <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.14.1</version> </depend

  • Spring spel获取自定义注解参数值方式

    目录 spel获取自定义注解参数值 1.注解类 2.注解使用 3.aop中处理 spel在注解中的使用 1 语法说明 2. 基本用法 4 #{…}和${…} spel获取自定义注解参数值 1.注解类 package com.xxx.mall.order.service.component;  import java.lang.annotation.*;   /**  * 库存不足等信息监控  * Created by xdc on 2019/4/16 15:43  */ @Retention(R

  • Java实现redis分布式锁的三种方式

    目录 一.引入原因 二.分布式锁实现过程中的问题 问题一:异常导致锁没有释放 问题二:获取锁与设置过期时间操作不是原子性的 问题三:锁过期之后被别的线程重新获取与释放 问题四:锁的释放不是原子性的 问题五:其他的问题? 三.具体实现 1. RedisTemplate 2. RedisLockRegistry 3. 使用redisson实现分布式锁 一.引入原因 在分布式服务中,常常有如定时任务.库存更新这样的场景. 在定时任务中,如果不使用quartz这样的分布式定时工具,只是简单的使用定时器来

  • SpringBoot利用注解来实现Redis分布式锁

    目录 一.业务背景 二.分析流程 加锁 超时问题 解决方案:增加一个「续时」 三.设计方案 四.实操 相关属性类配置 核心切面拦截的操作 五.开始测试 六.总结 一.业务背景 有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数据库的数据进行操作,需要避免对之前的业务造成影响. 二.分析流程 使用 Redis 作为分布式锁,将锁的状态放到 Redis 统一维护,解决集群中单机 JVM 信息不互通的问题,规定操作顺序,保护用户的数据正确. 梳理设计流程 新建注解 @interface

随机推荐