当前位置: 首页> 娱乐> 影视 > 营销型企业网站开发_建立时间和保持时间_南京seo排名优化_百度公司是国企还是私企

营销型企业网站开发_建立时间和保持时间_南京seo排名优化_百度公司是国企还是私企

时间:2025/7/14 0:44:03来源:https://blog.csdn.net/weixin_73093777/article/details/147010762 浏览次数:0次
营销型企业网站开发_建立时间和保持时间_南京seo排名优化_百度公司是国企还是私企

概述

使用自定义注解+ Spring AOP 切面编程 封装分布式锁逻辑,并通过抽象分布式锁工厂,解耦分布式锁具体实现,可以通过配置来动态切换具体分布式锁实例

初始化配置
spring:profiles:active: dev#redis配置redis:database: 0host: xxxport: 6379timeout: 5000password: xxx#分布式锁+限流redisson:single-server:address: redis://xxxpassword: xxxdatabase: 0timeout: 3000distributed:lock:type: "redisson"      # 启用Redisson实现redisson:default-wait-time: 10     # 覆盖默认等待时间default-lease-time: 30    # 覆盖默认租约时间default-lock-type: FAIR   # 默认使用公平锁
redis:arrange:type: "single"      # 可选 single/cluster
redisson配置类
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;@Configuration
//条件注解,只有当distributed.lock.type属性值为redisson时,该配置类才会生效
@ConditionalOnProperty(name = "distributed.lock.type", havingValue = "redisson")
public class RedissonConfig {//单例模式@Bean(name = "redissonClient")@ConditionalOnProperty(name = "redis.arrange.type", havingValue = "single")public RedissonClient singleRedissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://xxx").setTimeout(3000).setPassword("151212").setDatabase(0);return  Redisson.create(config);   //注意这里要返回redissonClient,和名称对应,因为@Resource会按名称找到bean注入}//集群模式@Bean(name = "redissonClient")@ConditionalOnProperty(name = "redis.arrange.type", havingValue = "cluster")public RedissonClient clusterRedissonClient(){Config config = new Config();ClusterServersConfig clusterServersConfig = config.useClusterServers();clusterServersConfig.setNodeAddresses(Arrays.asList("redis://xxxx"));return Redisson.create(config);}
}
通用锁接口

对外提供通用的分布式锁基本操作

public interface DistributedLock {boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException; //尝试加锁 非阻塞void lock(long leaseTime, TimeUnit unit);  //加锁 阻塞void unlock();     //解锁boolean isLocked();    //检查锁的状态boolean isHeldByThread(long threadId);   //锁是否被指定线程持有boolean isHeldByCurrentThread();   //锁是否被当前线程持有
}
工厂接口

对外提供统一的 获取分布式锁入口,不需要关心内部的锁实现

public interface DistributedLockFactory {/*** 根据key和锁类型 获取分布式锁*/DistributedLock getDistributedLock(String key, String lockType);
}
工厂实现类

工厂实现类就是基于redisson封装了创建分布式锁的工厂类逻辑,后续可以有多个实现类,比如基于zookeeper的

import io.lettuce.core.RedisException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Component
//条件注解,表示只有当配置文件中的属性 `distributed.lock.type` 的值为redisson时,这个类才会被加载到 Spring 容器中
//允许在不同的环境中切换不同的分布式锁实现(例如 Redisson、Zookeeper等)(如测试环境降级为本地锁),提升环境适配能力
//例如,ZooKeeper 的锁可能需要不同的检查逻辑,但通过接口可以统一对外暴露
@ConditionalOnProperty(name = "distributed.lock.type", havingValue = "redisson")
@Slf4j
public class RedissonLockFactory implements DistributedLockFactory {private final Logger logger = LoggerFactory.getLogger(RedissonLockFactory.class);@Resourceprivate RedissonClient redissonClient;// 从配置文件读取默认值(带默认值)@Value("${distributed.lock.redisson.default-wait-time:10}")private long defaultWaitTime;@Value("${distributed.lock.redisson.default-lease-time:30}") private long defaultLeaseTime;@Value("${distributed.lock.redisson.default-lock-type:FAIR}")private String defaultLockType;// 根据配置选择锁类型private RLock chooseLockType(String key, String lockType) {return "FAIR".equalsIgnoreCase(lockType) ?redissonClient.getFairLock(key) :redissonClient.getLock(key);}//根据key获取分布式锁@Overridepublic DistributedLock getDistributedLock(String key, String lockType) {// 根据配置选择锁类型RLock rLock = chooseLockType(key,lockType);//匿名内部类封装:避免了开发者在业务中直接操作`RLock`对象,防止因误调用`unlock()`导致锁泄漏或跨线程释放问题return new DistributedLock() {@Overridepublic boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {if (waitTime < 0 || leaseTime < 0) {throw new IllegalArgumentException("参数无效");}// 约定:传入0,使用默认值处理  传入具体值,则使用用户自定义值//实现了默认值与用户自定义值的动态切换long actualWait = waitTime == 0 ? defaultWaitTime : waitTime;long actualLease = leaseTime == 0 ? defaultLeaseTime : leaseTime;//非阻塞 尝试加锁boolean success = rLock.tryLock(actualWait, actualLease, unit);logger.info("{} 尝试加锁 结果:{}", key, success);return success;}//阻塞当前线程直到成功上锁@Overridepublic void lock(long leaseTime, TimeUnit unit) {long actualLease = leaseTime == 0 ? defaultLeaseTime : leaseTime;rLock.lock(actualLease, unit);logger.debug("{} 成功加锁", key);}@Overridepublic void unlock() {// 使用一次性检查,减少竞态条件try {if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {logger.debug("Unlocking key: {}", key);rLock.unlock();} else {logger.warn("试图解锁当前线程未持有的锁: {}", key);}} catch (RedisException e) {logger.error("释放锁时发生异常: {}", key, e);}}//判断当前锁的状态@Overridepublic boolean isLocked() {return rLock.isLocked();}//判断指定线程是否持有锁@Overridepublic boolean isHeldByThread(long threadId) {return rLock.isHeldByThread(threadId);}//判断当前线程是否持有锁@Overridepublic boolean isHeldByCurrentThread() {return rLock.isHeldByCurrentThread();}};}
}
为什么使用匿名内部类?

主要原因为了快速实现接口,并将其直接作为方法的返回值,无需单独定义一个具体的实现类,减少创建类的数量

直接通过匿名内部类实现了 DistributedLock 接口,并将 Redisson 的锁功能封装在其中,可以将 Redisson 的具体实现封装起来

另外就是所有与分布式锁相关的逻辑都集中在 工厂实现类中,便于维护和管理。

而且考虑到分布式锁其实也没有那么多复杂方面,所以就没考虑单独写实现类了

与直接使用 Redisson 的对比
对比维度直接使用 Redisson 的 RLock封装后的调用方式
依赖关系业务代码直接依赖 Redisson 的具体类(RLockRedissonClient)。业务代码仅依赖抽象接口 DistributedLock,与具体实现解耦。
扩展性若需切换实现(如 ZooKeeper),需修改所有调用 Redisson 的代码。通过配置即可切换实现,无需修改业务代码。
配置管理Redisson 的配置(如连接参数、超时时间)分散在业务代码中。Redisson 的配置集中管理在 Spring 配置文件中,统一维护。
日志与监控无内置日志记录锁的获取状态,需手动添加日志。封装后的 tryLock 方法自动记录锁的获取结果(如 logger.info)。
安全性和健壮性unlock 方法可能被误释放(未检查是否持有锁) 1. 开发者可能在未确认锁状态的情况下直接调用了 unlock() 2. 如果在加锁和解锁之间发生了异常,可能会导致锁的状态不一致封装后的 unlock 方法会检查锁是否由当前线程持有,避免误释放。
可维护性代码耦合度高,难以维护和测试。代码更清晰,便于维护和单元测试(可通过 Mock DistributedLock 接口)。
未来扩展新增功能(如锁统计、重试机制)需修改所有调用处。新增功能可通过扩展接口和工厂实现,业务代码无需改动。

直接使用 Redisson 的 RLock

@Autowired
private RedissonClient redissonClient;public void someMethod() {RLock rLock = redissonClient.getLock("myKey");try {// 直接调用 Redisson 的 tryLockif (rLock.tryLock(10, 30, TimeUnit.SECONDS)) {// 执行业务逻辑System.out.println("Lock acquired, performing operations...");}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 直接调用 Redisson 的 unlockrLock.unlock();}
}

封装后的解决方案:

@Autowired
private DistributedLockFactory lockFactory;public void someMethod() {// 通过工厂获取分布式锁实例DistributedLock lock = lockFactory.getDistributedLock("myKey");// 尝试加锁(使用默认值)调用匿名内部类封装好的tryLock方法if (lock.tryLock(0, 0, TimeUnit.SECONDS)) {try {// 执行业务逻辑System.out.println("Lock acquired, performing operations...");} finally {// 确保释放锁lock.unlock();}} else {System.out.println("Failed to acquire lock.");}
}
总结

通过抽象接口 DistributedLock 和工厂模式 DistributedLockFactory,可以实现以下优势:

  • 依赖接口而非实现:业务代码仅依赖 DistributedLock 接口,与具体实现(Redisson、ZooKeeper 等)解耦。

  • 动态切换实现:通过 @ConditionalOnProperty 注解,可以在 Spring Boot 的配置文件中动态指定锁的类型(如 distributed.lock.type=redissonzookeeper)。Spring 容器会根据配置加载对应的工厂类(如 RedissonLockFactoryZooKeeperLockFactory),而业务代码无需任何修改。

    • 添加新实现:实现 DistributedLockFactory 的子类 ZooKeeperLockFactory

    • 配置切换:在 application.properties 中修改配置

    • distributed.lock.type=zookeeper
  • 零侵入性:业务代码只需通过 DistributedLockFactory 获取锁实例,无需感知具体实现。

AOP封装 分布式注解
自定义注解

import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {/*** 锁key(支持SpEL表达式) * 示例:"order:lock:#{#order.id}"*/String key()default "";/*** 锁等待时间(秒)* 默认值0表示使用配置文件中的默认等待时间*/long waitTime() default 0;    /*** 锁持有时间(秒)* 默认值0表示使用配置文件中的默认租约时间*/long leaseTime() default 0;  /*** 锁类型(支持NORMAL/FAIR)* 默认值为NORMAL*/String lockType() default "NORMAL";  // 覆盖配置中的默认值//加锁失败策略  默认抛异常LockFailStrategy failStrategy() default LockFailStrategy.THROW_EXCEPTION;enum LockFailStrategy {THROW_EXCEPTION,  // 抛出异常RETURN_NULL,      // 返回空值RETRY             // 重试(需配合重试机制)}  
}
AOP切面
import io.lettuce.core.RedisException;
import io.lettuce.core.dynamic.support.ParameterNameDiscoverer;
import io.lettuce.core.dynamic.support.StandardReflectionParameterNameDiscoverer;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.concurrent.TimeUnit;@Aspect
@Component
@Slf4j
public class DistributedLockAspect {// 分布式锁工厂接口,用于获取分布式锁实例private final DistributedLockFactory lockFactory;// SpEL表达式解析器,用于解析动态keyprivate final ExpressionParser parser = new SpelExpressionParser();//构造器注入分布式锁工厂@Autowiredpublic DistributedLockAspect(DistributedLockFactory lockFactory) {this.lockFactory = lockFactory;}/*** 环绕通知,匹配带有RedisLock注解的方法** @param joinPoint 方法切入点* @param redisLock 注解对象* @return 方法执行结果* @throws Throwable 抛出异常*/@Around("@annotation(redisLock)")public Object aroundLock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {//从ProceedingJoinPoint对象中获取目标方法的签名,并将其强制转换为MethodSignature类型MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取目标对象的方法Method method = signature.getMethod();//获取目标方法的参数数组Object[] args = joinPoint.getArgs();String lockKey = redisLock.key();if ("".equals(lockKey)) {//根据当前的类名+方法参数信息生成keylockKey = configKey(signature.getDeclaringType(), method).replaceAll("[^a-zA-Z0-9]", "");System.out.println(lockKey);} else {//支持SpEL表达式StandardEvaluationContext context = new StandardEvaluationContext();//将当前方法参数信息都存入到SpEl执行的上下文中DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();String[] parameterNames = discoverer.getParameterNames(method);for (int i = 0, len = parameterNames.length; i < len; i++) {context.setVariable(parameterNames[i], args[i]);}Expression expression = parser.parseExpression(lockKey);lockKey = expression.getValue(context, String.class);}// 获取锁类型(NORMAL/FAIR)String lockType = redisLock.lockType();// 通过工厂获取分布式锁实例DistributedLock lock = lockFactory.getDistributedLock(lockKey, lockType);// 处理等待时间和租约时间的默认值long waitTime = redisLock.waitTime();long leaseTime = redisLock.leaseTime();boolean locked = false;try {locked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); // 尝试获取锁if (!locked) {// 未获取到锁,执行失败策略return handleLockFailure(redisLock.failStrategy(), method);}log.info("线程 {} 成功获取锁 [{}]", Thread.currentThread().getName(), lockKey);return joinPoint.proceed(); // 执行目标方法}finally {lock.unlock(); // 确保释放锁log.info("线程 {} 释放锁 [{}]", Thread.currentThread().getName(), lockKey);}}private String configKey(Class<?> targetType, Method method) {StringBuilder builder = new StringBuilder();builder.append(targetType.getSimpleName());builder.append('#').append(method.getName()).append('(');for (Class<?> param : method.getParameterTypes()){builder.append(param.getSimpleName()).append(',');}if (method.getParameterTypes().length > 0){builder.deleteCharAt(builder.length() - 1);}return builder.append(')').toString();}//处理加锁失败策略private Object handleLockFailure(RedisLock.LockFailStrategy strategy, Method method) throws Exception {switch (strategy) {case THROW_EXCEPTION:// 抛出异常throw new IllegalArgumentException("未能获取分布式锁");case RETURN_NULL:// 返回nulllog.warn("方法 {} 加锁失败,返回空值", method.getName());return null;case RETRY:// 重试逻辑(需集成Spring Retry实现)log.error("重试策略尚未实现");throw new UnsupportedOperationException("重试策略尚未实现");default:// 不支持的策略throw new IllegalArgumentException("不支持的锁失败策略");}}
}
使用案例
@RestController
@RequestMapping("/esbook")
public class EsBookController {@ResourceEsBookService esBookService;@GetMapping("/stock")@RedisLock(key = "'esbook:'+ #id", // 动态生成锁keywaitTime = 30,         // 等待时间30秒leaseTime = 5,         // 租约时间5秒lockType = "FAIR",     // 使用公平锁failStrategy = RedisLock.LockFailStrategy.RETURN_NULL // 失败返回null)public String updateBookStoreNum(@RequestParam Integer id) {String s = esBookService.updateBookStoreNum(id);return s;}
}
@Service
public class EsBookServiceImpl extends ServiceImpl<EsBookMapper, EsBook>implements EsBookService{@Overridepublic String updateBookStoreNum(Integer id) {// 从数据库中获取图书库存EsBook book = baseMapper.selectById(id);if (book != null && book.getStoreNum() >=1) {// 更新库存book.setStoreNum(book.getStoreNum() - 1);baseMapper.updateById(book);System.out.println("库存更新成功,当前库存: " + book.getStoreNum());return "Success";}return "库存不足!";}
}
JMETER压测

JDBC Connection [HikariProxyConnection@844857863 wrapping com.mysql.cj.jdbc.ConnectionImpl@6cb120b2] will not be managed by Spring
==>  Preparing: UPDATE es_book SET name=?, cover=?, description=?, author=?, publisher=?, price=?, store_num=?, status=?, category_id=? WHERE id=?
==> Parameters: 别做傻瓜(String), bookcover/别做傻瓜.jpg(String), 这本书由著名美学家朱光潜编写,内容探讨了如何保持理性与清醒,避免做出愚昧决策,特别是在面对生活困境时如何保持自我判断的能力。书中结合了生活中的实际案例,强调反思和批判性思维的重要性。(String), 朱光潜(String), 北京出版社(String), 35.0(Double), 0(Integer), 0(Integer), 6(Integer), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4bd54fcd]
库存更新成功,当前库存: 0
2025-04-05 12:35:17.765  INFO 25860 --- [io-8111-exec-28] c.z.bean.common.DistributedLockAspect    : 线程 http-nio-8111-exec-28 释放锁 [esbook:1]
2025-04-05 12:35:17.799  INFO 25860 --- [io-8111-exec-29] c.z.bean.common.RedissonLockFactory      : esbook:1 尝试加锁 结果:true
2025-04-05 12:35:17.799  INFO 25860 --- [io-8111-exec-29] c.z.bean.common.DistributedLockAspect    : 线程 http-nio-8111-exec-29 成功获取锁 [esbook:1]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@307f6246] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1052281401 wrapping com.mysql.cj.jdbc.ConnectionImpl@6cb120b2] will not be managed by Spring
==>  Preparing: SELECT id,name,cover,description,author,publisher,price,store_num AS storeNum,status,category_id FROM es_book WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, cover, description, author, publisher, price, storeNum, status, category_id
<==        Row: 1, 别做傻瓜, bookcover/别做傻瓜.jpg, <<BLOB>>, 朱光潜, 北京出版社, 35.0, 0, 0, 6
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@307f6246]
2025-04-05 12:35:17.946  INFO 25860 --- [io-8111-exec-29] c.z.bean.common.DistributedLockAspect    : 线程 http-nio-8111-exec-29 释放锁 [esbook:1]
...

测试结果成功,没有出现超卖

关键字:营销型企业网站开发_建立时间和保持时间_南京seo排名优化_百度公司是国企还是私企

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: