大家好,又見面了,我是你們的朋友全棧君。
目錄
- Java分布式鎖
- 一、基于ReentrantLock鎖解決超賣問題(單體)
- 1.1、重要代碼
- 1.2、測試代碼
- 二、 基于資料庫的分布式鎖(分布式)
- 2.1、重要代碼
- 2.2、重要sql語句
- 2.3、測試
- 三、基于redis分布式鎖
- 3.1、重要代碼
- 3.2、yml配置
- 四、基于分布式鎖解決定時任務重複問題
- 4.1、封裝redis分布式鎖
- 4.2、重要代碼
- 4.3、解決任務重複
- 五、zookeeper分布式鎖代碼實作
- 5.1、重要代碼
- 5.2、測試代碼
- 六、基于curator分布式鎖(推薦)
- 6.1、Application啟動類
- 6.2、測試代碼
- 七、基于redisson分布式鎖(推薦)
- 7.1、測試代碼
- 八、springboot引入redisson(推薦)
- 8.1、配置檔案
- 8.2、測試檔案
- 九、完整的pom.xml
- 一、基于ReentrantLock鎖解決超賣問題(單體)
Java分布式鎖
方式 | 優點 | 缺點 |
---|---|---|
資料庫 | 實作簡單、易于了解 | 對資料庫壓力大 |
Redis | 易于了解 | 自己實作、不支援阻塞 |
Zookeeper | 支援阻塞 | 需了解Zookeeper、程式複雜 |
Curator(推薦) | 提供鎖的方法 | 依賴Zookeeper、強一緻 |
Redisson(推薦) | 提供鎖的方法,可阻塞 |
一、基于ReentrantLock鎖解決超賣問題(單體)
1.1、重要代碼
package com.example.distributedemo.service;
import com.example.distributedemo.dao.OrderItemMapper;
import com.example.distributedemo.dao.OrderInfoMapper;
import com.example.distributedemo.dao.ProductMapper;
import com.example.distributedemo.model.orderInfo;
import com.example.distributedemo.model.OrderItem;
import com.example.distributedemo.model.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Service
@Slf4j
public class OrderService {
@Resource
private OrderInfoMapper orderMapper;
@Resource
private OrderItemMapper orderItemMapper;
@Resource
private ProductMapper productMapper;
//購買商品id
private Integer purchaseProductId = 1;
//購買商品數量
private int purchaseProductNum = 1;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
private Lock lock = new ReentrantLock();
public Integer createOrder() throws Exception{
Product product = null;
lock.lock();
try {
// 根據事務定義TransactionDefinition,擷取事務
TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
product = productMapper.selectByPrimaryKey(purchaseProductId);
if (product==null){
// 復原事務
platformTransactionManager.rollback(transaction1);
throw new Exception("購買商品:"+purchaseProductId+"不存在");
}
// 商品目前庫存
Integer currentCount = product.getCount();
System.out.println(Thread.currentThread().getName()+"庫存數:"+currentCount);
// 校驗庫存
if (purchaseProductNum > currentCount){
// 復原事務
platformTransactionManager.rollback(transaction1);
throw new Exception("商品"+purchaseProductId+"僅剩"+currentCount+"件,無法購買");
}
Product product1 = new Product();
product1.setId(purchaseProductId);
product1.setCount(0);
productMapper.updateByPrimaryKeySelective(product1);
// 送出事務
platformTransactionManager.commit(transaction1);
}finally {
lock.unlock();
}
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
orderInfo order = new orderInfo();
order.setId(1);
order.setOrderName(product.getName());
order.setAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
order.setOrderStatus(1);//待處理
orderMapper.insertSelective(order);
OrderItem orderItem = new OrderItem();
orderItem.setId(1);
orderItem.setOrderId(order.getId());
orderItem.setProductId(product.getId());
orderItemMapper.insertSelective(orderItem);
platformTransactionManager.commit(transaction);
return order.getId();
}
}
複制
1.2、測試代碼
package com.example;
import com.example.distributedemo.DistributeDemoApplication;
import com.example.distributedemo.service.OrderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DistributeDemoApplication.class)
public class DistributeDemoApplicationTests {
@Autowired
private OrderService orderService;
@Test
public void concurrentOrder() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(5);
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i =0;i<5;i++){
es.execute(()->{
try {
cyclicBarrier.await();
Integer orderId = orderService.createOrder();
System.out.println("訂單id:"+orderId);
} catch (Exception e) {
e.printStackTrace();
}finally {
cdl.countDown();
}
});
}
cdl.await();
es.shutdown();
}
}
複制
二、 基于資料庫的分布式鎖(分布式)
2.1、重要代碼
package com.example.distributedemo.controller;
import com.example.distributedemo.dao.DistributeLockMapper;
import com.example.distributedemo.model.DistributeLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zhuzhaoman
* @date 2020/5/2 14:56
* @description 基于資料庫的分布式鎖
*/
@Slf4j
@RestController
public class DemoController {
@Resource
private DistributeLockMapper distributeLockMapper;
@RequestMapping("/singleLock")
@Transactional(rollbackFor = Exception.class)
public String singleLock() throws Exception {
log.info("已經進入方法!");
DistributeLock distributeLock = distributeLockMapper.selectDistributeLock("demo");
if (distributeLock == null){
throw new Exception("分布式鎖找不到");
}
log.info("已經進入了鎖!");
Thread.sleep(30000);
return "我已經執行完成!";
}
}
複制
2.2、重要sql語句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.distributedemo.dao.DistributeLockMapper">
<resultMap id="BaseResultMap" type="com.example.distributedemo.model.DistributeLock">
<!--
WARNING - @mbg.generated
-->
<id column="id" jdbcType="INTEGER" property="id" />
<result column="business_code" jdbcType="VARCHAR" property="businessCode" />
<result column="business_name" jdbcType="VARCHAR" property="businessName" />
</resultMap>
<select id="selectDistributeLock" resultType="com.example.distributedemo.model.DistributeLock">
select *from distribute_lock
where business_code = #{businessCode}
for update
</select>
</mapper>
複制
2.3、測試
http://localhost:8081/singleLock
http://localhost:8082/singleLock
三、基于redis分布式鎖
3.1、重要代碼
package com.example.distributedemo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.awt.print.Book;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 17:05
* @description 基于redis分布式鎖
*/
@RestController
@Slf4j
public class RedisLockController {
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/redisLock")
public String redisLock() throws InterruptedException {
log.info("我進入了方法");
String key = "redisKey";
String value = UUID.randomUUID().toString();
RedisCallback<Boolean> redisCallback = redisConnection -> {
// 設定NX
RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
// 設定過期時間
Expiration expiration = Expiration.seconds(30);
// 序列化key和value
byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
// 執行set nx 操作
Boolean result = redisConnection.set(redisKey, redisValue, expiration, setOption);
return result;
};
// 擷取分布式鎖
Boolean lock = (Boolean) redisTemplate.execute(redisCallback);
if (lock) {
log.info("我進入了鎖!");
Thread.sleep(15000);
}
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class);
List<String> keys = Arrays.asList(key);
Boolean result = (Boolean) redisTemplate.execute(redisScript, keys, value);
log.info("釋放鎖的結果:" + result);
log.info("方法執行完成");
return "方法執行完成";
}
}
複制
3.2、yml配置
spring.datasource.username=root
spring.datasource.password=1999114zzm
spring.datasource.url=jdbc:mysql://localhost:3306/fen_bu_shi_suo?serverTimezone=Asia/Shanghai&useSSL=false
mybatis.mapper-locations=/mapper/*.xml
logging.pattern.dateformat=HH:mm:ss
spring.redis.host=你ip
spring.redis.port=6379
複制
四、基于分布式鎖解決定時任務重複問題
4.1、封裝redis分布式鎖
package com.example.distributedemo.lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 17:46
* @description 描述
*/
@Slf4j
public class RedisLockUtils implements AutoCloseable{
private RedisTemplate redisTemplate;
private String key;
private String value;
// 機關:秒
private int expireTime;
public RedisLockUtils(RedisTemplate redisTemplate, String key, int expireTime) {
this.redisTemplate = redisTemplate;
this.key = key;
this.value = UUID.randomUUID().toString();
this.expireTime = expireTime;
}
/**
* 擷取分布式鎖
* @return
*/
public boolean getLock() {
RedisCallback<Boolean> redisCallback = redisConnection -> {
// 設定NX
RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
// 設定過期時間
Expiration expiration = Expiration.seconds(expireTime);
// 序列化key和value
byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
// 執行set nx 操作
Boolean result = redisConnection.set(redisKey, redisValue, expiration, setOption);
return result;
};
// 擷取分布式鎖
Boolean lock = (Boolean) redisTemplate.execute(redisCallback);
return lock;
}
/**
* 釋放鎖
* @return
*/
public boolean unLock() {
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class);
List<String> keys = Arrays.asList(key);
Boolean result = (Boolean) redisTemplate.execute(redisScript, keys, value);
log.info("釋放鎖的結果:" + result);
return result;
}
@Override
public void close() throws Exception {
// 釋放鎖
unLock();
}
}
複制
4.2、重要代碼
package com.example.distributedemo.controller;
import com.example.distributedemo.lock.RedisLockUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 17:05
* @description 描述
*/
@RestController
@Slf4j
public class RedisLockController {
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/redisLock")
public String redisLock() throws InterruptedException {
log.info("我進入了方法");
// 擷取鎖
try(RedisLockUtils redisLock = new RedisLockUtils(redisTemplate, "redisKey", 30)) {
boolean lock = redisLock.getLock();
if (lock) {
log.info("我進入了鎖!");
Thread.sleep(15000);
}
}catch (Exception e){
e.printStackTrace();
}
log.info("方法執行完成");
return "方法執行完成";
}
}
複制
4.3、解決任務重複
- 啟動類
package com.example.distributedemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@EnableScheduling // 開啟定時任務
public class DistributeDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DistributeDemoApplication.class, args);
}
}
複制
- 定時任務
package com.example.distributedemo.service;
import com.example.distributedemo.lock.RedisLockUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 18:17
* @description 描述
*/
@Service
@Slf4j
public class SchedulerService {
@Autowired
private RedisTemplate redisTemplate;
@Scheduled(cron = "0/5 * * * * ?")
public void sendSms() {
try(RedisLockUtils redisLock = new RedisLockUtils(redisTemplate, "autoSms", 30)) {
boolean lock = redisLock.getLock();
if (lock) {
log.info("向138xxxxx使用者發短信!");
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
複制
五、zookeeper分布式鎖代碼實作
5.1、重要代碼
package com.example.distributedemo.lock;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 18:52
* @description 描述
*/
@Slf4j
public class ZkLock implements AutoCloseable, Watcher {
private ZooKeeper zooKeeper;
private String znode;
public ZkLock() throws IOException {
this.zooKeeper = new ZooKeeper("49.232.26.207:2181", 60000, this);
}
public boolean getLock(String businessCode) {
try {
// 建立業務根節點
Stat stat = zooKeeper.exists("/" + businessCode, false);
if (stat==null){
zooKeeper.create("/" + businessCode,
businessCode.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
// 建立瞬時有序節點 /order/order_序号
znode = zooKeeper.create("/" + businessCode + "/" + businessCode + "_",
businessCode.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// 擷取業務節點下所有的子節點
List<String> childrenNodes = zooKeeper.getChildren("/" + businessCode, false);
// 子節點排序
Collections.sort(childrenNodes);
// 擷取序列号最小的(第一個)子節點
String firstNode = childrenNodes.get(0);
// 如果建立的節點時第一個子節點,則獲得鎖
if (znode.endsWith(firstNode)) {
return true;
}
// 不是第一個子節點,則監聽前一個節點
String lastNode = firstNode;
for (String node: childrenNodes){
if (znode.endsWith(node)) {
zooKeeper.exists("/" + businessCode + "/" + lastNode, true);
break;
} else {
lastNode = node;
}
}
synchronized (this) {
wait();
}
return true;
}catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
notify();
}
}
}
@Override
public void close() throws Exception {
zooKeeper.delete(znode, -1);
zooKeeper.close();
log.info("鎖被釋放");
}
}
複制
5.2、測試代碼
package com.example.distributedemo.controller;
import com.example.distributedemo.lock.ZkLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 19:45
* @description 描述
*/
@RestController
@Slf4j
public class ZookeeperController {
@RequestMapping("/zkLock")
public String zookeeperLock() {
log.info("我進入了方法");
try(ZkLock zkLock = new ZkLock()) {
boolean order = zkLock.getLock("order");
if (order) {
log.info("獲得了鎖!");
Thread.sleep(10000);
}
}catch (Exception e){
e.printStackTrace();
}
log.info("方法執行完成!");
return "方法執行完成!";
}
}
複制
六、基于curator分布式鎖(推薦)
6.1、Application啟動類
package com.example.distributedemo;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
public class DistributeDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DistributeDemoApplication.class, args);
}
@Bean(initMethod = "start", destroyMethod = "close")
public CuratorFramework getCuratorFramework() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(30000, 3);
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient("49.232.26.207:2181", retryPolicy);
return curatorFramework;
}
}
複制
6.2、測試代碼
package com.example.distributedemo.controller;
import com.example.distributedemo.lock.ZkLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 19:45
* @description 描述
*/
@RestController
@Slf4j
public class ZookeeperController {
@Autowired
private CuratorFramework curatorFramework;
@RequestMapping("/curatorLock")
public String curatorLock() throws Exception {
log.info("我進入了方法");
InterProcessMutex lock = new InterProcessMutex(curatorFramework, "/order");
try {
if (lock.acquire(30, TimeUnit.SECONDS)) {
log.info("我獲得了鎖!");
Thread.sleep(10000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
log.info("釋放了鎖");
lock.release();
}
log.info("方法執行完成!");
return "方法執行完成!";
}
}
複制
七、基于redisson分布式鎖(推薦)
7.1、測試代碼
package com.example.distributedemo.controller;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 16:53
* @description 描述
*/
@RestController
@Slf4j
public class RedissonLockController {
@RequestMapping("/redissonLock")
public String redissonLock() throws InterruptedException {
Config config = new Config();
config.useSingleServer().setAddress("redis://49.232.26.207:6379");
RedissonClient redissonClient = Redisson.create(config);
RLock rLock = redissonClient.getLock("order");
log.info("我進入了方法");
rLock.lock(30, TimeUnit.SECONDS);
log.info("獲得了鎖");
Thread.sleep(15000);
log.info("釋放了鎖");
rLock.unlock();
log.info("方法執行完成");
return "方法執行完成";
}
}
複制
八、springboot引入redisson(推薦)
8.1、配置檔案
spring.datasource.username=root
spring.datasource.password=1999114zzm
spring.datasource.url=jdbc:mysql://localhost:3306/fen_bu_shi_suo?serverTimezone=Asia/Shanghai&useSSL=false
mybatis.mapper-locations=/mapper/*.xml
logging.pattern.dateformat=HH:mm:ss
spring.redis.host=49.232.26.207
spring.redis.port=6379
複制
8.2、測試檔案
package com.example.distributedemo.controller;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author zhuzhaoman
* @date 2020/5/2 0002 16:53
* @description 描述
*/
@RestController
@Slf4j
public class RedissonLockController {
@Autowired
private RedissonClient redissonClient;
@RequestMapping("/redissonLock")
public String redissonLock() throws InterruptedException {
RLock rLock = redissonClient.getLock("order");
log.info("我進入了方法");
rLock.lock(30, TimeUnit.SECONDS);
log.info("獲得了鎖");
Thread.sleep(15000);
log.info("釋放了鎖");
rLock.unlock();
log.info("方法執行完成");
return "方法執行完成";
}
}
複制
九、完整的pom.xml
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>distribute-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>distribute-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.2</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</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-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
複制
釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/144795.html原文連結:https://javaforall.cn