@@ -1,5 +1,9 @@ | |||||
package com.xkcoding.distributed.lock.annotation; | package com.xkcoding.distributed.lock.annotation; | ||||
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; | import java.util.concurrent.TimeUnit; | ||||
/** | /** | ||||
@@ -10,6 +14,8 @@ import java.util.concurrent.TimeUnit; | |||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @date 2022-09-02 15:47 | * @date 2022-09-02 15:47 | ||||
*/ | */ | ||||
@Target({ElementType.METHOD}) | |||||
@Retention(RetentionPolicy.RUNTIME) | |||||
public @interface DLock { | public @interface DLock { | ||||
/** | /** | ||||
* @return 锁的标识,支持 spel 表达式 | * @return 锁的标识,支持 spel 表达式 | ||||
@@ -3,7 +3,7 @@ package com.xkcoding.distributed.lock.api.impl; | |||||
import com.xkcoding.distributed.lock.api.DistributedLock; | import com.xkcoding.distributed.lock.api.DistributedLock; | ||||
import com.xkcoding.distributed.lock.api.DistributedLockService; | import com.xkcoding.distributed.lock.api.DistributedLockService; | ||||
import com.xkcoding.distributed.lock.api.LockClient; | import com.xkcoding.distributed.lock.api.LockClient; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
@@ -18,7 +18,7 @@ import java.util.function.Supplier; | |||||
* @date 2022-09-02 21:41 | * @date 2022-09-02 21:41 | ||||
*/ | */ | ||||
@Slf4j | @Slf4j | ||||
@AllArgsConstructor | |||||
@RequiredArgsConstructor | |||||
public class DistributedLockServiceImpl implements DistributedLockService { | public class DistributedLockServiceImpl implements DistributedLockService { | ||||
private final LockClient lockClient; | private final LockClient lockClient; | ||||
@@ -1,7 +1,6 @@ | |||||
package com.xkcoding.distributed.lock.api.impl; | package com.xkcoding.distributed.lock.api.impl; | ||||
import com.xkcoding.distributed.lock.api.DistributedLock; | import com.xkcoding.distributed.lock.api.DistributedLock; | ||||
import org.jetbrains.annotations.NotNull; | |||||
import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
@@ -30,7 +29,7 @@ public class DummyDistributedLock extends DistributedLock { | |||||
} | } | ||||
@Override | @Override | ||||
public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { | |||||
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { | |||||
return true; | return true; | ||||
} | } | ||||
@@ -1,9 +1,11 @@ | |||||
package com.xkcoding.distributed.lock.autoconfigure; | package com.xkcoding.distributed.lock.autoconfigure; | ||||
import com.xkcoding.distributed.lock.aop.DistributedLockAspect; | |||||
import com.xkcoding.distributed.lock.api.DistributedLockService; | import com.xkcoding.distributed.lock.api.DistributedLockService; | ||||
import com.xkcoding.distributed.lock.api.LockClient; | import com.xkcoding.distributed.lock.api.LockClient; | ||||
import com.xkcoding.distributed.lock.api.impl.DistributedLockServiceImpl; | import com.xkcoding.distributed.lock.api.impl.DistributedLockServiceImpl; | ||||
import com.xkcoding.distributed.lock.api.impl.DummyDistributedLockClient; | import com.xkcoding.distributed.lock.api.impl.DummyDistributedLockClient; | ||||
import org.mybatis.spring.annotation.MapperScan; | |||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||||
import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||
import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||
@@ -17,7 +19,8 @@ import org.springframework.context.annotation.Configuration; | |||||
* @date 2022-09-02 21:57 | * @date 2022-09-02 21:57 | ||||
*/ | */ | ||||
@Configuration(proxyBeanMethods = false) | @Configuration(proxyBeanMethods = false) | ||||
public class DistributedLockConfiguration { | |||||
@MapperScan("com.xkcoding.distributed.lock.mapper") | |||||
public class DistributedLockAutoConfiguration { | |||||
@Bean | @Bean | ||||
@ConditionalOnMissingBean | @ConditionalOnMissingBean | ||||
public LockClient lockClient() { | public LockClient lockClient() { | ||||
@@ -28,4 +31,9 @@ public class DistributedLockConfiguration { | |||||
public DistributedLockService distributedLockService(LockClient lockClient) { | public DistributedLockService distributedLockService(LockClient lockClient) { | ||||
return new DistributedLockServiceImpl(lockClient); | return new DistributedLockServiceImpl(lockClient); | ||||
} | } | ||||
@Bean | |||||
public DistributedLockAspect distributedLockAspect(DistributedLockService distributedLockService) { | |||||
return new DistributedLockAspect(distributedLockService); | |||||
} | |||||
} | } |
@@ -0,0 +1,41 @@ | |||||
package com.xkcoding.distributed.lock.controller; | |||||
import com.xkcoding.distributed.lock.service.StockService; | |||||
import lombok.RequiredArgsConstructor; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
/** | |||||
* <p> | |||||
* 模拟仓库接口 | |||||
* </p> | |||||
* | |||||
* @author yangkai.shen | |||||
* @date 2022-09-03 00:46 | |||||
*/ | |||||
@RestController | |||||
@RequestMapping("/stock") | |||||
@RequiredArgsConstructor(onConstructor_ = @Autowired) | |||||
public class StockController { | |||||
private final StockService stockService; | |||||
/** | |||||
* 模拟减库存 | |||||
*/ | |||||
@GetMapping("/reduce") | |||||
public String reduceStock() { | |||||
stockService.reduceStock(1L); | |||||
return "reduce success"; | |||||
} | |||||
/** | |||||
* 模拟减库存 | |||||
*/ | |||||
@GetMapping("/reset") | |||||
public String resetStock() { | |||||
stockService.resetStock(); | |||||
return "reset success"; | |||||
} | |||||
} |
@@ -2,7 +2,6 @@ package com.xkcoding.distributed.lock.mapper; | |||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||||
import com.xkcoding.distributed.lock.model.Stock; | import com.xkcoding.distributed.lock.model.Stock; | ||||
import org.apache.ibatis.annotations.Mapper; | |||||
/** | /** | ||||
* 货物 Mapper | * 货物 Mapper | ||||
@@ -10,6 +9,5 @@ import org.apache.ibatis.annotations.Mapper; | |||||
* @author yangkai.shen | * @author yangkai.shen | ||||
* @date 2022-09-02 14:09 | * @date 2022-09-02 14:09 | ||||
*/ | */ | ||||
@Mapper | |||||
public interface StockMapper extends BaseMapper<Stock> { | public interface StockMapper extends BaseMapper<Stock> { | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
package com.xkcoding.distributed.lock.service; | package com.xkcoding.distributed.lock.service; | ||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||||
import com.xkcoding.distributed.lock.annotation.DLock; | import com.xkcoding.distributed.lock.annotation.DLock; | ||||
import com.xkcoding.distributed.lock.mapper.StockMapper; | import com.xkcoding.distributed.lock.mapper.StockMapper; | ||||
import com.xkcoding.distributed.lock.model.Stock; | import com.xkcoding.distributed.lock.model.Stock; | ||||
@@ -26,7 +26,7 @@ public class StockService { | |||||
/** | /** | ||||
* 减货物 | * 减货物 | ||||
*/ | */ | ||||
@DLock(lockKey = "lock_stock_${stockId}", lockTime = 3000, timeUnit = TimeUnit.MICROSECONDS) | |||||
@DLock(lockKey = "'lock_stock_'+#stockId", lockTime = 3000, timeUnit = TimeUnit.MICROSECONDS) | |||||
public void reduceStock(Long stockId) { | public void reduceStock(Long stockId) { | ||||
// 先查询库存是否充足 | // 先查询库存是否充足 | ||||
Stock stock = this.stockMapper.selectById(stockId); | Stock stock = this.stockMapper.selectById(stockId); | ||||
@@ -44,7 +44,7 @@ public class StockService { | |||||
public void resetStock() { | public void resetStock() { | ||||
log.info("start to init stock data..."); | log.info("start to init stock data..."); | ||||
stockMapper.delete(new LambdaQueryWrapper<Stock>().gt(Stock::getId, 0)); | |||||
stockMapper.delete(new QueryWrapper<Stock>().gt("id", 0)); | |||||
Stock mockStock = new Stock(); | Stock mockStock = new Stock(); | ||||
mockStock.setId(1L); | mockStock.setId(1L); | ||||
@@ -0,0 +1,35 @@ | |||||
package org.springframework.core; | |||||
import org.springframework.lang.Nullable; | |||||
import java.io.IOException; | |||||
/** | |||||
* <p> | |||||
* 1. springboot 3.x 版本依赖 spring-core 6.x,里面没有这个异常 | |||||
* 2. mybatis-plus 自动装配 {@see com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean} 又需要这个异常 | |||||
* </p> | |||||
* | |||||
* @author yangkai.shen | |||||
* @date Created in 2022-09-03 00:42 | |||||
*/ | |||||
public class NestedIOException extends IOException { | |||||
public NestedIOException(String msg) { | |||||
super(msg); | |||||
} | |||||
public NestedIOException(@Nullable String msg, @Nullable Throwable cause) { | |||||
super(msg, cause); | |||||
} | |||||
@Override | |||||
@Nullable | |||||
public String getMessage() { | |||||
return NestedExceptionUtils.buildMessage(super.getMessage(), this.getCause()); | |||||
} | |||||
static { | |||||
NestedExceptionUtils.class.getName(); | |||||
} | |||||
} |
@@ -1,2 +1,2 @@ | |||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | ||||
com.xkcoding.distributed.lock.autoconfigure.DistributedLockConfiguration | |||||
com.xkcoding.distributed.lock.autoconfigure.DistributedLockAutoConfiguration |
@@ -0,0 +1,9 @@ | |||||
CREATE TABLE `db_stock` | |||||
( | |||||
`id` bigint(20) NOT NULL AUTO_INCREMENT, | |||||
`name` varchar(255) DEFAULT NULL COMMENT '货物名称', | |||||
`count` bigint(11) DEFAULT NULL COMMENT '库存量', | |||||
PRIMARY KEY (`id`) | |||||
) ENGINE = InnoDB | |||||
AUTO_INCREMENT = 1 | |||||
DEFAULT CHARSET = utf8 COMMENT ='Spring Boot Demo 分布式锁-模拟库存表'; |