最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+Redis實現(xiàn)分布式鎖的深度剖析

 更新時間:2026年01月08日 08:22:40   作者:悟空碼字  
分布式鎖是在分布式系統(tǒng)中協(xié)調(diào)多個節(jié)點對共享資源進行互斥訪問的機制,這篇文章主要為大家詳細介紹了如何使用SpringBoot和Redis實現(xiàn)分布式鎖,感興趣的小伙伴可以了解下

一、分布式鎖概述

1.1 什么是分布式鎖

分布式鎖是在分布式系統(tǒng)中協(xié)調(diào)多個節(jié)點對共享資源進行互斥訪問的機制。當多個服務(wù)實例需要訪問共享資源時,分布式鎖能確保同一時刻只有一個實例可以執(zhí)行關(guān)鍵代碼段。

1.2 分布式鎖的特性

  • 互斥性:同一時刻只有一個客戶端能持有鎖
  • 可重入性:同一個客戶端可以多次獲取同一把鎖
  • 鎖超時:避免死鎖,鎖應(yīng)有自動過期機制
  • 高可用:鎖服務(wù)需要高可用,避免單點故障
  • 高性能:獲取和釋放鎖的操作應(yīng)該高效

1.3 常見實現(xiàn)方式

  • 基于數(shù)據(jù)庫(如MySQL唯一索引)
  • 基于Redis(推薦,性能好)
  • 基于ZooKeeper(可靠性高)
  • 基于ETCD

二、基于Redis的分布式鎖實現(xiàn)(推薦)

2.1 項目結(jié)構(gòu)

src/main/java/com/example/distributedlock/
├── DistributedLockApplication.java
├── config/
│   ├── RedisConfig.java
│   └── RedissonConfig.java
├── service/
│   ├── DistributedLockService.java
│   └── impl/
│       ├── RedisDistributedLock.java
│       └── RedissonDistributedLock.java
├── annotation/
│   └── DistributedLock.java
├── aspect/
│   └── DistributedLockAspect.java
└── controller/
    └── TestController.java

2.2 添加依賴

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Redisson -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.23.2</version>
    </dependency>
    
    <!-- AOP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2.3 配置文件

# application.yml
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms

# Redisson配置(可選)
redisson:
  config: |
    singleServerConfig:
      address: "redis://localhost:6379"
      database: 1

2.4 基于RedisTemplate的實現(xiàn)

Redis配置

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 設(shè)置序列化
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), 
            ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerializer.setObjectMapper(om);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jacksonSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

分布式鎖接口

public interface DistributedLockService {
    
    /**
     * 獲取鎖
     * @param lockKey 鎖key
     * @param requestId 請求標識(用于釋放鎖時驗證)
     * @param expireTime 過期時間(毫秒)
     * @param waitTime 等待時間(毫秒)
     * @return 是否獲取成功
     */
    boolean tryLock(String lockKey, String requestId, long expireTime, long waitTime);
    
    /**
     * 釋放鎖
     * @param lockKey 鎖key
     * @param requestId 請求標識
     * @return 是否釋放成功
     */
    boolean releaseLock(String lockKey, String requestId);
    
    /**
     * 自動續(xù)期的鎖
     * @param lockKey 鎖key
     * @param requestId 請求標識
     * @param expireTime 過期時間
     * @param waitTime 等待時間
     * @param action 要執(zhí)行的業(yè)務(wù)邏輯
     * @return 執(zhí)行結(jié)果
     */
    <T> T tryLockWithAutoRenew(String lockKey, String requestId, 
                               long expireTime, long waitTime, 
                               Supplier<T> action);
}

Redis分布式鎖實現(xiàn)

@Service
@Slf4j
public class RedisDistributedLock implements DistributedLockService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String LOCK_PREFIX = "LOCK:";
    private static final String LUA_SCRIPT_RELEASE = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "   return redis.call('del', KEYS[1]) " +
        "else " +
        "   return 0 " +
        "end";
    
    @Override
    public boolean tryLock(String lockKey, String requestId, 
                          long expireTime, long waitTime) {
        String key = LOCK_PREFIX + lockKey;
        long start = System.currentTimeMillis();
        
        try {
            while (System.currentTimeMillis() - start < waitTime) {
                // 使用SET命令實現(xiàn)原子性操作:NX表示不存在時才設(shè)置,PX設(shè)置過期時間
                Boolean success = redisTemplate.opsForValue()
                    .setIfAbsent(key, requestId, expireTime, TimeUnit.MILLISECONDS);
                
                if (Boolean.TRUE.equals(success)) {
                    log.info("獲取鎖成功, key: {}, requestId: {}", key, requestId);
                    return true;
                }
                
                // 等待隨機時間后重試,避免驚群效應(yīng)
                Thread.sleep(getRandomWaitTime());
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("獲取鎖被中斷", e);
        }
        
        return false;
    }
    
    @Override
    public boolean releaseLock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        
        // 使用Lua腳本保證原子性:只有requestId匹配時才刪除
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(LUA_SCRIPT_RELEASE);
        script.setResultType(Long.class);
        
        Long result = redisTemplate.execute(script, 
            Collections.singletonList(key), requestId);
        
        boolean success = result != null && result == 1;
        if (success) {
            log.info("釋放鎖成功, key: {}, requestId: {}", key, requestId);
        } else {
            log.warn("釋放鎖失敗, key: {}, requestId: {}", key, requestId);
        }
        
        return success;
    }
    
    @Override
    public <T> T tryLockWithAutoRenew(String lockKey, String requestId, 
                                     long expireTime, long waitTime, 
                                     Supplier<T> action) {
        if (!tryLock(lockKey, requestId, expireTime, waitTime)) {
            throw new RuntimeException("獲取分布式鎖失敗");
        }
        
        // 啟動自動續(xù)期線程
        AutoRenewTask renewTask = new AutoRenewTask(lockKey, requestId, expireTime);
        Thread renewThread = new Thread(renewTask);
        renewThread.setDaemon(true);
        renewThread.start();
        
        try {
            // 執(zhí)行業(yè)務(wù)邏輯
            return action.get();
        } finally {
            // 停止續(xù)期并釋放鎖
            renewTask.stop();
            releaseLock(lockKey, requestId);
        }
    }
    
    /**
     * 自動續(xù)期任務(wù)
     */
    private class AutoRenewTask implements Runnable {
        private final String lockKey;
        private final String requestId;
        private final long expireTime;
        private volatile boolean running = true;
        
        public AutoRenewTask(String lockKey, String requestId, long expireTime) {
            this.lockKey = lockKey;
            this.requestId = requestId;
            this.expireTime = expireTime;
        }
        
        public void stop() {
            this.running = false;
        }
        
        @Override
        public void run() {
            String key = LOCK_PREFIX + lockKey;
            // 續(xù)期間隔設(shè)置為過期時間的1/3
            long renewInterval = expireTime / 3;
            
            while (running) {
                try {
                    Thread.sleep(renewInterval);
                    
                    // 檢查鎖是否存在且requestId匹配,然后續(xù)期
                    Object currentRequestId = redisTemplate.opsForValue().get(key);
                    if (requestId.equals(currentRequestId)) {
                        redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
                        log.debug("鎖續(xù)期成功, key: {}", key);
                    } else {
                        log.warn("鎖已不屬于當前請求, 停止續(xù)期");
                        break;
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
    
    private long getRandomWaitTime() {
        return (long) (Math.random() * 100 + 50); // 50-150ms隨機等待
    }
}

2.5 基于Redisson的實現(xiàn)(更推薦)

Redisson配置

@Configuration
public class RedissonConfig {
    
    @Value("${spring.redis.host:localhost}")
    private String redisHost;
    
    @Value("${spring.redis.port:6379}")
    private int redisPort;
    
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress(String.format("redis://%s:%d", redisHost, redisPort))
              .setDatabase(0)
              .setConnectionPoolSize(10)
              .setConnectionMinimumIdleSize(5)
              .setTimeout(3000);
        
        return Redisson.create(config);
    }
}

Redisson分布式鎖實現(xiàn)

@Service
@Primary
@Slf4j
public class RedissonDistributedLock implements DistributedLockService {
    
    @Autowired
    private RedissonClient redissonClient;
    
    @Override
    public boolean tryLock(String lockKey, String requestId, 
                          long expireTime, long waitTime) {
        RLock lock = redissonClient.getLock("LOCK:" + lockKey);
        
        try {
            // Redisson自動處理鎖續(xù)期和重入
            return lock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("獲取鎖被中斷", e);
            return false;
        }
    }
    
    @Override
    public boolean releaseLock(String lockKey, String requestId) {
        RLock lock = redissonClient.getLock("LOCK:" + lockKey);
        
        // 檢查鎖是否被當前線程持有
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
            log.info("Redisson鎖釋放成功, key: {}", lockKey);
            return true;
        }
        return false;
    }
    
    @Override
    public <T> T tryLockWithAutoRenew(String lockKey, String requestId, 
                                     long expireTime, long waitTime, 
                                     Supplier<T> action) {
        RLock lock = redissonClient.getLock("LOCK:" + lockKey);
        
        try {
            if (lock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS)) {
                try {
                    return action.get();
                } finally {
                    // Redisson會自動續(xù)期,這里只需要在業(yè)務(wù)完成后釋放
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            } else {
                throw new RuntimeException("獲取分布式鎖失敗");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("獲取鎖被中斷", e);
        }
    }
}

2.6 注解方式使用分布式鎖

定義注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    
    /**
     * 鎖的key,支持SpEL表達式
     */
    String key() default "";
    
    /**
     * 鎖前綴
     */
    String prefix() default "LOCK:";
    
    /**
     * 過期時間(毫秒),默認30秒
     */
    long expire() default 30000;
    
    /**
     * 等待時間(毫秒),默認3秒
     */
    long waitTime() default 3000;
    
    /**
     * 是否使用公平鎖
     */
    boolean fair() default false;
    
    /**
     * 獲取鎖失敗時的錯誤信息
     */
    String errorMsg() default "系統(tǒng)繁忙,請稍后重試";
}

實現(xiàn)切面

@Aspect
@Component
@Slf4j
public class DistributedLockAspect {
    
    @Autowired
    private DistributedLockService distributedLockService;
    
    @Autowired
    private RedissonClient redissonClient;
    
    private static final String REQUEST_ID_PREFIX = "LOCK_";
    private static final AtomicLong REQUEST_COUNTER = new AtomicLong(0);
    
    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, 
                        DistributedLock distributedLock) throws Throwable {
        
        // 生成請求ID
        String requestId = generateRequestId();
        
        // 解析鎖的key(支持SpEL)
        String lockKey = resolveLockKey(joinPoint, distributedLock);
        String fullLockKey = distributedLock.prefix() + lockKey;
        
        // 獲取鎖
        RLock lock = null;
        if (distributedLock.fair()) {
            lock = redissonClient.getFairLock(fullLockKey);
        } else {
            lock = redissonClient.getLock(fullLockKey);
        }
        
        boolean locked = false;
        try {
            // 嘗試獲取鎖
            locked = lock.tryLock(distributedLock.waitTime(), 
                                 distributedLock.expire(), 
                                 TimeUnit.MILLISECONDS);
            
            if (!locked) {
                log.warn("獲取分布式鎖失敗, key: {}", fullLockKey);
                throw new RuntimeException(distributedLock.errorMsg());
            }
            
            log.info("獲取分布式鎖成功, key: {}, requestId: {}", 
                    fullLockKey, requestId);
            
            // 執(zhí)行目標方法
            return joinPoint.proceed();
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("獲取鎖被中斷", e);
        } finally {
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("釋放分布式鎖, key: {}", fullLockKey);
            }
        }
    }
    
    private String generateRequestId() {
        String localIp = getLocalIp();
        long timestamp = System.currentTimeMillis();
        long sequence = REQUEST_COUNTER.incrementAndGet();
        return String.format("%s_%s_%d_%d", 
            REQUEST_ID_PREFIX, localIp, timestamp, sequence);
    }
    
    private String getLocalIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return "127.0.0.1";
        }
    }
    
    private String resolveLockKey(ProceedingJoinPoint joinPoint, 
                                 DistributedLock distributedLock) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        Object target = joinPoint.getTarget();
        
        // 如果key為空,使用類名+方法名
        String key = distributedLock.key();
        if (StringUtils.isEmpty(key)) {
            return target.getClass().getName() + ":" + method.getName();
        }
        
        // 解析SpEL表達式
        if (key.contains("#")) {
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            
            EvaluationContext context = new StandardEvaluationContext();
            context.setVariable("args", args);
            
            // 設(shè)置參數(shù)名
            String[] paramNames = signature.getParameterNames();
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
            
            return expression.getValue(context, String.class);
        }
        
        return key;
    }
}

2.7 使用示例

@RestController
@RequestMapping("/api/test")
@Slf4j
public class TestController {
    
    @Autowired
    private DistributedLockService distributedLockService;
    
    /**
     * 方式1:手動使用鎖
     */
    @PostMapping("/order/{orderId}")
    public ResponseEntity<?> createOrder(@PathVariable String orderId) {
        String requestId = UUID.randomUUID().toString();
        String lockKey = "order_create:" + orderId;
        
        // 使用自動續(xù)期的鎖
        String result = distributedLockService.tryLockWithAutoRenew(
            lockKey, 
            requestId, 
            30000,  // 30秒過期
            3000,   // 等待3秒
            () -> {
                // 業(yè)務(wù)邏輯
                log.info("處理訂單創(chuàng)建, orderId: {}", orderId);
                try {
                    Thread.sleep(1000); // 模擬業(yè)務(wù)處理
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return "訂單創(chuàng)建成功";
            }
        );
        
        return ResponseEntity.ok(result);
    }
    
    /**
     * 方式2:使用注解
     */
    @DistributedLock(
        key = "'inventory:' + #productId",  // 使用SpEL
        expire = 30000,
        waitTime = 5000,
        errorMsg = "庫存操作過于頻繁,請稍后重試"
    )
    @PostMapping("/inventory/{productId}")
    public ResponseEntity<?> updateInventory(
            @PathVariable String productId,
            @RequestParam int quantity) {
        
        log.info("更新庫存, productId: {}, quantity: {}", productId, quantity);
        // 業(yè)務(wù)邏輯
        try {
            Thread.sleep(2000); // 模擬業(yè)務(wù)處理
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return ResponseEntity.ok("庫存更新成功");
    }
    
    /**
     * 方式3:扣減庫存示例(防超賣)
     */
    @DistributedLock(
        key = "'product_stock:' + #productId",
        expire = 10000,
        waitTime = 3000
    )
    @PostMapping("/deduct/{productId}")
    public ResponseEntity<?> deductStock(
            @PathVariable String productId,
            @RequestParam int deductQuantity) {
        
        // 這里應(yīng)該是數(shù)據(jù)庫操作,為了示例使用偽代碼
        log.info("扣減庫存, productId: {}, quantity: {}", 
                productId, deductQuantity);
        
        // 1. 查詢當前庫存
        // 2. 檢查庫存是否充足
        // 3. 更新庫存
        // 4. 記錄流水
        
        return ResponseEntity.ok(Map.of(
            "success", true,
            "productId", productId,
            "deducted", deductQuantity
        ));
    }
}

2.8 主啟動類

@SpringBootApplication
@EnableAspectJAutoProxy
public class DistributedLockApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(DistributedLockApplication.class, args);
    }
}

三、其他實現(xiàn)方式

3.1 基于數(shù)據(jù)庫的分布式鎖

@Service
@Slf4j
public class DatabaseDistributedLock implements DistributedLockService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public boolean tryLock(String lockKey, String requestId, 
                          long expireTime, long waitTime) {
        long start = System.currentTimeMillis();
        String sql = "INSERT INTO distributed_lock(lock_key, request_id, " +
                    "expire_time, create_time) VALUES (?, ?, ?, ?)";
        
        while (System.currentTimeMillis() - start < waitTime) {
            try {
                // 清理過期鎖
                cleanExpiredLocks();
                
                // 嘗試插入記錄(利用唯一索引實現(xiàn)互斥)
                jdbcTemplate.update(sql, lockKey, requestId, 
                    System.currentTimeMillis() + expireTime, 
                    System.currentTimeMillis());
                return true;
            } catch (DuplicateKeyException e) {
                // 鎖已存在,等待后重試
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    return false;
                }
            }
        }
        return false;
    }
    
    @Override
    public boolean releaseLock(String lockKey, String requestId) {
        String sql = "DELETE FROM distributed_lock WHERE lock_key = ? " +
                    "AND request_id = ?";
        int rows = jdbcTemplate.update(sql, lockKey, requestId);
        return rows > 0;
    }
    
    private void cleanExpiredLocks() {
        String sql = "DELETE FROM distributed_lock WHERE expire_time < ?";
        jdbcTemplate.update(sql, System.currentTimeMillis());
    }
}

四、總結(jié)

4.1 實現(xiàn)要點總結(jié)

原子性保證

  • Redis使用SET NX PX命令或Lua腳本
  • Redisson內(nèi)部已實現(xiàn)原子操作
  • 數(shù)據(jù)庫使用唯一索引

避免死鎖

  • 必須設(shè)置鎖超時時間
  • 實現(xiàn)自動續(xù)期機制
  • finally塊中確保鎖釋放

鎖續(xù)期機制

  • 對于長任務(wù)需要自動續(xù)期
  • 續(xù)期間隔應(yīng)小于鎖過期時間
  • 續(xù)期前驗證鎖所有權(quán)

可重入性

  • Redisson天然支持可重入鎖
  • 手動實現(xiàn)需要記錄重入次數(shù)

4.2 方案對比

方案優(yōu)點缺點適用場景
Redis性能高,實現(xiàn)簡單需要保證Redis高可用高并發(fā)場景
Redisson功能完善,自動續(xù)期依賴Redisson客戶端生產(chǎn)環(huán)境推薦
數(shù)據(jù)庫無需額外組件性能差,有死鎖風(fēng)險低頻操作
ZooKeeper可靠性高性能一般,部署復(fù)雜強一致性場景

4.3 最佳實踐建議

  • 鎖粒度要小:盡量縮小鎖的范圍,減少鎖持有時間
  • 合理設(shè)置超時:根據(jù)業(yè)務(wù)執(zhí)行時間合理設(shè)置鎖超時時間
  • 監(jiān)控告警:監(jiān)控鎖的獲取失敗率和等待時間
  • 降級方案:鎖服務(wù)不可用時要有降級策略
  • 鍵名設(shè)計:使用有意義的鎖鍵名,便于排查問題
  • 避免鎖嵌套:盡量避免分布式鎖嵌套使用

4.4 生產(chǎn)環(huán)境注意事項

  • Redis集群模式:使用Redlock算法或Redisson的MultiLock
  • 網(wǎng)絡(luò)分區(qū):考慮腦裂問題,設(shè)置合理的超時時間
  • 性能監(jiān)控:監(jiān)控Redis內(nèi)存、CPU和網(wǎng)絡(luò)延遲
  • 容災(zāi)方案:主從切換時可能導(dǎo)致鎖失效,需要評估風(fēng)險

4.5 推薦方案

對于大多數(shù)SpringBoot項目,推薦使用Redisson實現(xiàn)分布式鎖,因為:

  • 功能完善,支持多種鎖類型(可重入、公平、讀寫鎖等)
  • 自動續(xù)期機制,避免業(yè)務(wù)未完成鎖已過期
  • 與SpringBoot良好集成
  • 社區(qū)活躍,文檔完善

通過以上實現(xiàn),可以在SpringBoot應(yīng)用中可靠地實現(xiàn)分布式鎖,解決分布式環(huán)境下的資源競爭問題。

到此這篇關(guān)于SpringBoot+Redis實現(xiàn)分布式鎖的深度剖析的文章就介紹到這了,更多相關(guān)SpringBoot Redis分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解析spring事務(wù)管理@Transactional為什么要添加rollbackFor=Exception.class

    解析spring事務(wù)管理@Transactional為什么要添加rollbackFor=Exception.class

    這篇文章主要介紹了spring事務(wù)管理@Transactional為什么要添加rollbackFor=Exception.class,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • SpringBoot集成quartz實現(xiàn)定時任務(wù)

    SpringBoot集成quartz實現(xiàn)定時任務(wù)

    這篇文章主要介紹了如何使用SpringBoot整合Quartz,并將定時任務(wù)寫入庫中(持久化存儲),還可以任意對定時任務(wù)進行如刪除、暫停、恢復(fù)等操作,需要的可以了解下
    2023-09-09
  • springboot中的靜態(tài)資源加載順序優(yōu)先級

    springboot中的靜態(tài)資源加載順序優(yōu)先級

    這篇文章主要介紹了springboot中的靜態(tài)資源加載順序優(yōu)先級,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 淺談MyBatis-Plus學(xué)習(xí)之Oracle的主鍵Sequence設(shè)置的方法

    淺談MyBatis-Plus學(xué)習(xí)之Oracle的主鍵Sequence設(shè)置的方法

    這篇文章主要介紹了淺談MyBatis-Plus學(xué)習(xí)之Oracle的主鍵Sequence設(shè)置的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 深入理解java代碼實現(xiàn)分治算法

    深入理解java代碼實現(xiàn)分治算法

    分治算法是一種遞歸算法,它將問題劃分為幾個獨立的子問題,然后遞歸地解決這些子問題,最后將子問題的解合并起來得到原問題的解,本文詳細的介紹java分治算法,感興趣的可以了解一下
    2023-09-09
  • java中為什么要謹慎使用Arrays.asList、ArrayList的subList

    java中為什么要謹慎使用Arrays.asList、ArrayList的subList

    這篇文章主要介紹了java中為什么要謹慎使用Arrays.asList、ArrayList的subList,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • JAVA使用ffmepg處理視頻的方法(壓縮,分片,合并)

    JAVA使用ffmepg處理視頻的方法(壓縮,分片,合并)

    這篇文章主要介紹了JAVA使用ffmepg處理視頻的方法,包括視頻壓縮分片合并功能,通過實例代碼講解的很詳細,對java ffmepg處理視頻相關(guān)知識感興趣的朋友一起看看吧
    2021-05-05
  • SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片

    SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片

    這篇文章主要介紹了SpringBoot3實現(xiàn)上傳圖片并返回路徑讓前端顯示圖片,文中通過圖文和代碼講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-12-12
  • Kafka?日志存儲實現(xiàn)過程

    Kafka?日志存儲實現(xiàn)過程

    這篇文章主要為大家介紹了Kafka?日志存儲的實現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • SpringBoot實現(xiàn)Tomcat集群的會話管理功能

    SpringBoot實現(xiàn)Tomcat集群的會話管理功能

    在使用 Tomcat 集群時,由于每個 Tomcat 實例的 Session 存儲是獨立的,導(dǎo)致無法實現(xiàn) Session 的共享,這可能影響到用戶跨節(jié)點的訪問,為了實現(xiàn)跨 Tomcat 實例共享 Session,可以使用 Spring Session 配合 Redis 進行集中式會話管理,需要的朋友可以參考下
    2024-12-12

最新評論

砀山县| 应用必备| 石阡县| 彭州市| 吉木萨尔县| 马鞍山市| 河东区| 东丽区| 大竹县| 泰顺县| 长顺县| 常山县| 西城区| 玉山县| 宝鸡市| 塘沽区| 金乡县| 五大连池市| 酉阳| 科尔| 贡山| 阳朔县| 屯门区| 隆安县| 东兰县| 万宁市| 额济纳旗| 陆川县| 克山县| 凌云县| 松江区| 名山县| 四子王旗| 革吉县| 康平县| 志丹县| 昌邑市| 高台县| 泗阳县| 岑溪市| 庆城县|