Rioc 欢迎来到小晨儿的博客

分布式锁简单实现

  • 2024-08-13

分布式锁简单实现

package com.tangyh.basic.cache.lock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
public class RedisLock {
    
/** * redis超时时间 */
public static final int LOCK_EXPIRE = 1000*10;// ms
@Autowiredprivate StringRedisTemplate redisTemplate;

/**
 * 分布式锁
 *
 * @param key key值
 * @return 是否获取到
 */
 public boolean lock(String key) {
    String lock = key;
    try {
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            long expireAt = System.currentTimeMillis() + LOCK_EXPIRE;
            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
            if (acquire) {
                return true;
            } else {
                //判断该key上的值是否过期了
                byte[] value = connection.get(lock.getBytes());
                if (Objects.nonNull(value) && value.length > 0) {
                    long expireTime = Long.parseLong(new String(value));
                    if (expireTime < System.currentTimeMillis()) {
                        // 如果锁已经过期
                        byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE).getBytes());
                        // 防止死锁
                        return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                    }
                }
            }
            return false;
        });
    } finally {
        RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
    }
 }

/**
 * 删除锁
 *
 * @param key
 */
 public void delete(String key) {
    try {
        redisTemplate.delete(key);
    } finally {
        RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
    }
 }
}
  1. 首先,定义了一个 lock 字符串,作为锁的键名。
  2. 使用 redisTemplate.execute 方法执行 Redis 回调操作,在回调函数中进行分布式锁的获取逻辑。
  3. 在回调函数内部,计算了锁的过期时间 expireAt,并通过 Redis 的 setNX 命令尝试设置键值对,如果设置成功(即获取到了锁),则返回 true
  4. 如果 setNX 返回 false,说明锁已经被其他客户端持有,则尝试检查该锁的过期时间是否已经过期。
  5. 如果锁已经过期,则使用 getSet 命令更新锁的过期时间,并返回 true,表示成功获取到了锁。这里使用 getSet 命令是为了保证更新过期时间的原子性,防止出现竞态条件。
  6. 如果锁未过期,则返回 false,表示未能成功获取到锁。

delete 方法中,执行删除锁操作:

  1. 使用 redisTemplate.delete 方法删除指定键名的锁。
  2. finally 块中,无论是否发生异常,都会执行 RedisConnectionUtils.unbindConnection 方法释放 Redis 连接。

这样,无论是获取锁还是删除锁,在操作完成后都会正确释放 Redis 连接,避免了连接资源泄漏的问题。

MybatisPlus的InnerInterceptor接口

  • 2024-08-13

MybatisPlus的InnerInterceptor接口

public interface InnerInterceptor {
    default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return true;
    }

    default void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    }

    default boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        return true;
    }

    default void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
    }

    default void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
    }

    default void beforeGetBoundSql(StatementHandler sh) {
    }

    default void setProperties(Properties properties) {
    }
    
    default Executor pluginExecutor(Executor executor) {
        return executor;
    }
}

willDoQuery: 当执行查询操作时调用,判断是否需要执行查询操作。默认返回true,表示继续执行查询操作,如果需要阻止查询操作,则可以在实现该方法时返回false。
beforeQuery: 在执行查询操作之前调用。可以在查询之前进行一些必要的操作,例如设置数据范围、修改 SQL 等。
willDoUpdate: 当执行更新操作时调用,判断是否需要执行更新操作。默认返回true,表示继续执行更新操作,如果需要阻止更新操作,则可以在实现该方法时返回false。
beforeUpdate: 在执行更新操作之前调用。通过实现该方法,可以在更新之前进行一些必要的操作,例如设置更新时间、加密数据等。
beforePrepare: 在执行 SQL 之前调用。可以在 SQL 执行之前进行一些必要的操作,例如设置事务隔离级别、设置查询超时时间等。
beforeGetBoundSql: 在获取 BoundSql 对象之前调用。可以在获取 BoundSql 对象之前进行一些必要的操作,例如设置参数、修改 SQL 等。
setProperties: 设置拦截器属性。该方法在创建拦截器实例时调用,用于设置拦截器的属性。
pluginExecutor: 在创建 Executor 对象时调用,可以在这里对 Executor 对象进行包装或其他处理。

深度解析context-path参数的作用与应用

  • 2024-08-13

深度解析context-path参数的作用与应用

一、引言

​ 在 Web 开发中,context-path 参数是一个常见且重要的概念。它在实际应用中扮演着重要的角色,对于理解和设计 Web 应用程序具有深远的影响。本文将从深度和广度两个维度对context-path 参数进行全面评估,以帮助大家更好地理解和应用这一概念。

二、概念

​ context-path 参数通常用于标识 Web 应用程序在 Web 服务器上的根路径。当一个 Web 应用程序被部署到 Web 服务器上时,它会被分配一个唯一的 context-path 参数,用于标识该应用程序在服务器上的位置。在实际应用中,context-path 参数通常是一个URL路径的一部分,用于定位和访问特定的 Web 应用程序。

三、作用

1.作为 Web 应用程序的唯一标识符

context-path 参数可以帮助 Web 服务器将不同的 Web 应用程序区分开来。通过在 URL中加入 context-path 参数,用户可以访问特定的 Web 应用程序,并且可以避免 URL 冲突的问题。

2.管理 Web 应用程序的资源

通过 context-path 参数,Web 应用程序可以统一管理自身的资源。可以通过 context-path 参数来访问 Web 应用程序的静态文件、配置文件以及其他资源,从而实现统一的资源管理和访问。

3.支持多个 Web 应用程序的部署

在一个 Web 服务器中,可能会部署多个 Web 应用程序。通过使用不同的 context-path 参数,可以实现多个 Web 应用程序之间的隔离和独立运行,从而提高了 Web 服务器的灵活性和可维护性。

四、应用场景

1.在 Spring MVC 框架中的应用

在 Spring MVC 框架中,context-path 参数经常被用于标识和访问不同的控制器和请求处理器。通过配置不同的 context-path 参数,可以实现不同的功能模块和业务逻辑之间的分离和管理。

2.在 Java Web 应用程序中的应用

​ 在Java Web 应用程序中,context-path 参数通常被用于配置和管理 Web 应用程序的路由、过滤器和拦截器。通过合理地使用context-path 参数,可以实现 Web 应用程序的灵活定位和访问控制。

3.在前端开发中的应用

​ 在前端开发中,context-path 参数可以被用于管理和访问 Web应用程序的静态资源,如JavaScript、CSS、图片等。通过使用context-path 参数,可以实现前端资源的统一管理和访问。

五、总结

​ 整体来看,context-path 参数在 Web 开发中扮演着十分关键的角色,它不仅可以帮助 Web 服务器管理和定位 Web 应用程序,还可以支持多个 Web 应用程序的部署和运行。在实际应用中,合理地使用context-path 参数可以提高 Web 应用程序的灵活性和可维护性,从而更好地满足用户需求。
​ context-path 参数作为一个重要的 Web 开发概念,对于开发人员来说是必须深入理解和应用的。希望通过本文的介绍和解析,大家能够对 context-path 参数有一个更深入和全面的理解,从而更好地应用于实际项目中。

hello

  • 2024-04-27

hello!
这是一个开始~

    1