springboot集成integration实现注解与声明式加锁

1 篇文章 0 订阅
1 篇文章 0 订阅

Integration在基于Spring的应用程序中实现轻量级消息传递,并支持通过声明适配器与外部系统集成。 Spring Integration的主要目标是提供一个简单的模型来构建企业集成解决方案,同时保持关注点的分离,这对于生成可维护,可测试的代码至关重要。我们熟知的 Spring Cloud Stream的底层就是Spring Integration。

Spring Integration提供的全局锁目前为如下存储提供了实现:

  • Gemfire
  • JDBC
  • Redis
  • Zookeeper

它们使用相同的API抽象,这意味着,不论使用哪种存储,你的编码体验是一样的。试想一下你目前是基于zookeeper实现的分布式锁,哪天你想换成redis的实现,我们只需要修改相关依赖和配置就可以了,无需修改代码。下面是你使用 Spring Integration 实现分布式锁时需要关注的方法:

所以本文章旨在搭建基础的全局锁Integration,后面的分布式锁可以基于此实现,让代码更加优雅解耦

全局锁与分布式锁的区别

  • 全局锁(Global Lock): 全局锁作用于单个进程或单个实例,控制了整个进程或实例中的资源访问。在多线程应用程序中,全局锁可以确保在同一进程中不同线程之间的并发访问互斥。
  • 分布式锁(Distributed Lock): 分布式锁用于跨多个进程或多个节点的分布式系统中,用于控制不同进程或节点之间的并发访问。它可以确保在整个分布式系统中的不同节点之间的互斥访问。

本章分为两部分进行(声明式与自定义注解)

首先定义依赖

声明式

声明式的可以直接用,我创建了一个多线程任务去执行这个加锁解锁逻辑如下

加锁后打印一句话然后再解锁,那怎么判断这样是正确的?每一个线程加锁和解锁必定在一起,因为是同一把锁,第二个线程只要第一个线程解锁后才能拿到锁

  • obtain():根据名字获取锁
  • tryLock()尝试加锁

配置一个本地锁

@SpringBootConfiguration
@Slf4j
public class LocalLockConfig {

    /**
     * 配置本地锁z
     * @return
     */

    @Bean
    public LockRegistry localLockRegistry(){
        LockRegistry lockRegistry = new DefaultLockRegistry();
        log.info("the local lock is loaded successfully!");
        return lockRegistry;
    }
}

测试

   /**
     * 测试手动获取锁
     */
    @Test
    public void lockRegistryTest() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for(int i = 0; i < 10; i++){

            threadPoolTaskExecutor.execute(()->{
                Lock lock = lockRegistry.obtain(LockConstants.R_PAN_LOCK);
                boolean lockResult = false;
                try {
                    lockResult = lock.tryLock(60L, TimeUnit.SECONDS);

                    if(lockResult) {
                        System.out.println(Thread.currentThread().getName() + " get the lock");

                    }

                } catch (InterruptedException e) {
                   throw new RuntimeException(e);

                }finally {

                    if(lockResult) {
                        System.out.println(Thread.currentThread().getName() + " release the lock");
                        lock.unlock();
                    }

                }

                countDownLatch.countDown();
            });

        }

        countDownLatch.await();
    }

打印如下说明没问题

自定义注解

定义一个核心模块,架构如下

首先是定义一个注解类

  • 定义锁的名称
  • 锁过期时长
  • 自定义锁的key
  • 锁的生成器
  • @Documented(当一个注解被标记时,如果你使用该注解标记了一个类、方法或字段,那么在生成 Java 文档时,这个注解的信息将会包含在文档中,方便其他开发者查阅。)
    @Retention(RetentionPolicy.RUNTIME)(表示作用于运行)
    @Target({ElementType.METHOD})(表示作用于方法上面)
/**
 * 自定义锁的注解
 * @author yubin
 * @create 2024-04-16-14:25
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Lock {


    /**
     * 锁的名称
     * @return
     */
    String name() default "";


    /**
     * 锁的过期时长
     * @return
     */
    long expireSecond() default  60L;


    /**
     * 自定义锁的key,支持el表达式
     * @return
     */
    String[] keys() default {};


    /**
     * 制定锁的生成器
     * @return
     */
    Class<? extends KeyGenerator> keyGenerator() default StandardKeyGenerator.class;

}

定义了一些参数如下

public interface LockConstants {

    /**
     * 公用lock的名称
     */
    String R_PAN_LOCK = "r-pan-lock";


    /**
     * 公用lock的path
     * 主要针对zk等节点型的软件
     */
    String R_PAN_LOCK_PATH = "/r-pan-lock";

}

接着定义一个类封装锁的上下文信息(里面参数都是跟方法类有关系的)

  • init(ProceedingJoinPoint proceedingJoinPoint) 方法是一个静态工厂方法,用于初始化 LockContext 对象。它接收一个 ProceedingJoinPoint 对象作为参数,该对象包含了切点方法的执行上下文信息,并返回一个初始化好的 LockContext 对象。

  • doInit(LockContext lockContext, ProceedingJoinPoint proceedingJoinPoint) 方法是一个私有静态方法,用于执行 LockContext 对象的初始化过程。它接收一个 LockContext 对象和一个 ProceedingJoinPoint 对象作为参数,将切点方法的相关信息解析并设置到 LockContext 对象的属性中。

  • ProceedingJoinPoint 是 Spring AOP 中的一个接口,它代表了正在执行的连接点(Join Point),并提供了许多方法来获取连接点的相关信息。简单来说就是要被增强的方法,可以获取他的信息

@Data
public class LockContext {

    /**
     * 切面方法所属类的名称
     */
    private String className;

    /**
     * 切点方法的名称
     */
    private String methodName;

    /**
     * 切点方法上面标准的自定义锁注解
     */
    private Lock annotation;

    /**
     * 类的class对象
     */
    private Class classType;

    /**
     * 当前调用的方法的实体
     */
    private Method method;


    /**
     * 参数列表实体
     */
    private Object[] args;

    /**
     * 参数列表类型
     */
    private Class[] parameterTypes;


    /**
     * 代理对象实体
     */
    private Object target;


    /**
     * 初始化实体对象
     * @param proceedingJoinPoint
     * @return
     */
    public static LockContext init(ProceedingJoinPoint proceedingJoinPoint){

        LockContext lockContext = new LockContext();

        doInit(lockContext,proceedingJoinPoint);
        return lockContext;

    }

    private static void doInit(LockContext lockContext, ProceedingJoinPoint proceedingJoinPoint) {

        Signature signature = proceedingJoinPoint.getSignature();

        Object[] args = proceedingJoinPoint.getArgs();

        Object target = proceedingJoinPoint.getTarget();

        String methodName = signature.getName();

        Class classType = signature.getDeclaringType();

        String className = signature.getDeclaringTypeName();


        Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();

        Method method = ((MethodSignature) signature).getMethod();

        Lock annotation = method.getAnnotation(Lock.class);

        lockContext.setArgs(args);
        lockContext.setTarget(target);
        lockContext.setMethodName(methodName);
        lockContext.setClassType(classType);
        lockContext.setClassName(className);
        lockContext.setParameterTypes(parameterTypes);
        lockContext.setMethod(method);
        lockContext.setAnnotation(annotation);

    }
}

接着前面的锁提到了需要锁的生成器,接下我我们定义锁的生成器

这种一般都是需要写接口->抽象类->实体类来进行

首先是接口,定义了一个方法返回string的字符串

public interface KeyGenerator {


    /**
     * 生成锁的key
     * @param lockContext
     * @return
     */
    String generateKey(LockContext lockContext);


    

}

接着定义抽象类,这是key生成器的公用父类,里面可以实现公用的方法

通过这个抽象类,可以方便地定义和实现不同类型的锁键生成器,只需要继承 AbstractKeyGenerator 类,并实现 doGenerateKey() 方法,即可定制化地生成符合业务需求的锁键值。

  • generateKey(LockContext lockContext) 方法是实现了 KeyGenerator 接口中定义的生成锁键的方法。该方法接收一个 LockContext 对象作为参数,其中包含了切面方法的上下文信息,例如方法名、类名、方法参数等。然后根据 LockContext 中的信息,生成锁的键值。
  • generateKey() 方法中,首先从 lockContext 中获取切点方法上的 Lock 注解,然后获取该注解中定义的键(keys)数组。

  • 接着创建了一个空的 KeyValueMap,用于保存键值对。然后遍历 keys 数组,利用 SpEL 表达式工具类(SpElUtil)根据注解中定义的键,从 lockContext 中获取对应的值,并将键值对放入 KeyValueMap 中。

  • KeyValueMap 存储了 SpEL 表达式中键值对的 HashMap,其中键是 SpEL 表达式中定义的键,而值是根据 SpEL 表达式求值得到的结果。

  • 最后调用了 doGenerateKey() 方法,该方法是一个抽象方法,需要子类实现具体的键生成逻辑。它接收 lockContext 对象和 KeyValueMap 参数,并返回最终生成的锁键值。

public abstract class AbstractKeyGenerator implements KeyGenerator{

    @Override
    public String generateKey(LockContext lockContext) {
        Lock annotation = lockContext.getAnnotation();

        String[] keys = annotation.keys();

        Map<String,String> KeyValueMap = Maps.newHashMap();

        if(ArrayUtils.isNotEmpty(keys)){
            Arrays.stream(keys).forEach(key->{
                KeyValueMap.put(key, SpElUtil.getStringValue(key,lockContext.getClassName(),lockContext.getMethodName(),lockContext.getClassType(),
                        lockContext.getMethod(),lockContext.getArgs(),lockContext.getParameterTypes(),lockContext.getTarget()));
            });
        }


        return doGenerateKey(lockContext,KeyValueMap);

    }

    /**
     * 具体逻辑下层到子类实现
     * @param lockContext
     * @param keyValueMap
     * @return
     */
    protected abstract String doGenerateKey(LockContext lockContext, Map<String, String> keyValueMap);

}

接着介绍一个工具类,可以解析SpEL表达式的,让注解可以支持SpEL表达式

这个工具类 SpElUtil 主要用于解析 SpEL(Spring Expression Language)表达式,并根据表达式获取相应的值。让我来逐步解释这个类的功能和结构:

  1. SpElUtil 类提供了三个静态方法,用于解析 SpEL 表达式并获取对应的值:

    • getCustomerValue(): 解析 SpEL 表达式,并将结果转换为指定类型。
    • getValue(): 解析 SpEL 表达式,获取结果但不进行类型转换。
    • getStringValue(): 解析 SpEL 表达式,并将结果转换为字符串类型。
  2. 在这些方法中,都需要传入以下参数:

    • spElExpression: 要解析的 SpEL 表达式。
    • className: 类的名称。
    • methodName: 方法的名称。
    • classType: 类的 Class 对象。
    • method: 方法的 Method 对象。
    • args: 方法的参数。
    • parameterTypes: 方法的参数类型。
    • target: 目标对象。
  3. 这个类内部使用了 RPanExpressionEvaluator 内部类来实现 SpEL 表达式的解析和缓存。RPanExpressionEvaluator 继承了 CachedExpressionEvaluator 类,用于缓存解析过的表达式,提高解析效率。

  4. RPanExpressionRootObject 是一个私有静态内部类,用于作为表达式的根对象,提供了一些常见的表达式格式的支持,例如 #root.className#root.methodName 等。

  5. RPanExpressionEvaluator 内部类中,使用了 ConcurrentHashMap 来缓存解析过的表达式和目标方法,以提高解析效率。

总的来说,这个工具类提供了一组方法,用于方便地解析 SpEL 表达式,并根据表达式获取相应的值,可用于动态地获取对象的属性、方法参数等信息

/**
 * 解析SpEl表达式解析器工具类
 */
public class SpElUtil {

    private static final RPanExpressionEvaluator expressionEvaluator = new RPanExpressionEvaluator();

    /**
     * 解析SpEl表达式
     *
     * @param spElExpression
     * @param returnType
     * @param className
     * @param methodName
     * @param classType
     * @param method
     * @param args
     * @param parameterTypes
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T getCustomerValue(String spElExpression,
                                         Class<T> returnType,
                                         String className,
                                         String methodName,
                                         Class classType,
                                         Method method,
                                         Object[] args,
                                         Class[] parameterTypes,
                                         Object target) {
        EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(className, methodName, classType, method, args, parameterTypes, target);
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, classType);
        return expressionEvaluator.getValueWithCustomerType(spElExpression, methodKey, evaluationContext, returnType);
    }

    /**
     * 解析SpEll表达式
     *
     * @param spElExpression
     * @param className
     * @param methodName
     * @param classType
     * @param method
     * @param args
     * @param parameterTypes
     * @param target
     * @return
     */
    public static Object getValue(String spElExpression,
                                  String className,
                                  String methodName,
                                  Class classType,
                                  Method method,
                                  Object[] args,
                                  Class[] parameterTypes,
                                  Object target) {
        EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(className, methodName, classType, method, args, parameterTypes, target);
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, classType);
        return expressionEvaluator.getValue(spElExpression, methodKey, evaluationContext);
    }

    /**
     * 解析SpEl表达式
     *
     * @param spElExpression
     * @param className
     * @param methodName
     * @param classType
     * @param method
     * @param args
     * @param parameterTypes
     * @param target
     * @return
     */
    public static String getStringValue(String spElExpression,
                                        String className,
                                        String methodName,
                                        Class classType,
                                        Method method,
                                        Object[] args,
                                        Class[] parameterTypes,
                                        Object target) {
        EvaluationContext evaluationContext = expressionEvaluator.createEvaluationContext(className, methodName, classType, method, args, parameterTypes, target);
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, classType);
        return expressionEvaluator.getValueWithStringType(spElExpression, methodKey, evaluationContext);
    }

    /**
     * 表达式根对象
     * 该对象主要支持以下表达式格式:
     * #root.className
     * #root.methodName
     * #root.classType
     * #root.method
     * #root.args
     * #root.parameterTypes
     * #root.target
     */
    @Data
    @AllArgsConstructor
    private static class RPanExpressionRootObject {

        /**
         * 切点方法所属类名称
         */
        private String className;

        /**
         * 切点方法名称
         */
        private String methodName;

        /**
         * 类的Class
         */
        private Class classType;

        /**
         * 代理方法实体
         */
        private Method method;

        /**
         * 切点方法传参
         */
        private Object[] args;

        /**
         * 切点方法传参类型
         */
        private Class[] parameterTypes;

        /**
         * 代理对象实体
         */
        private Object target;

    }

    /**
     * 表达式执行器对象
     */
    @Data
    private static class RPanExpressionEvaluator extends CachedExpressionEvaluator {
        private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
        private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(256);
        private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(256);

        /**
         * 创建表达式执行器上下文对象
         *
         * @param className
         * @param methodName
         * @param classType
         * @param method
         * @param args
         * @param parameterTypes
         * @param target
         * @return
         */
        private EvaluationContext createEvaluationContext(String className,
                                                          String methodName,
                                                          Class classType,
                                                          Method method,
                                                          Object[] args,
                                                          Class[] parameterTypes,
                                                          Object target) {
            Method targetMethod = getTargetMethod(classType, method);
            RPanExpressionRootObject root = new RPanExpressionRootObject(className, methodName, classType, method, args, parameterTypes, target);
            return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
        }

        /**
         * 表达式解析,解析结果自动转化成指定类型
         *
         * @param conditionExpression
         * @param elementKey
         * @param evalContext
         * @param clazz
         * @return
         */
        public <T> T getValueWithCustomerType(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> clazz) {
            return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
        }

        /**
         * 表达式解析,解析结果不自动转化
         *
         * @param conditionExpression
         * @param elementKey
         * @param evalContext
         * @return
         */
        public Object getValue(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
            Expression expression = getExpression(this.conditionCache, elementKey, conditionExpression);
            return expression.getValue(evalContext);
        }

        /**
         * 表达式解析,解析结果自动转化成String
         *
         * @param conditionExpression
         * @param elementKey
         * @param evalContext
         * @return
         */
        public String getValueWithStringType(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
            Object value = getValue(conditionExpression, elementKey, evalContext);
            if (Objects.nonNull(value)) {
                return value.toString();
            }
            return StringUtils.EMPTY;
        }

        /**
         * 获取缓存的目标方法
         *
         * @param targetClass
         * @param method
         * @return
         */
        private Method getTargetMethod(Class<?> targetClass, Method method) {
            AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
            Method targetMethod = this.targetMethodCache.get(methodKey);
            if (targetMethod == null) {
                targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
                if (targetMethod == null) {
                    targetMethod = method;
                }
                this.targetMethodCache.put(methodKey, targetMethod);
            }
            return targetMethod;
        }
    }

}

接着实现这个父类定义了一个标准默认的生成锁的类(就是为了获取唯一的标识方法的东西,通过方法的上下文信息以及SPEL表达式解析出来的东西)

  • 这段代码是一个具体的锁键生成器类 StandardKeyGenerator,它继承自之前提到的抽象类 AbstractKeyGenerator。在这个类中,实现了抽象方法 doGenerateKey(),用于生成标准格式的锁键值。这样生成的锁键值能够唯一标识某个方法的调用,并且考虑了方法的参数和动态生成的值,以保证锁的精确性。
  • 这段代码首先创建了一个列表 keyList,用于存储生成的锁键值的各个部分。然后将类名和方法名添加到 keyList 中,以构成锁的唯一标识的一部分。接着,获取切点方法的参数类型数组 parameterTypes,如果参数类型数组不为空,则将每个参数类型转换为字符串并添加到 keyList 中;如果参数类型数组为空,则将字符串表示的 Void.class 类型添加到 keyList 中,表示该方法没有参数。随后,将 SpEL 表达式解析得到的键值对中的值添加到 keyList 中。最后,将 keyList 中的各个部分用逗号连接起来,形成最终的锁键值,并返回。
@Component
public class StandardKeyGenerator extends AbstractKeyGenerator{


    /**
     * 标准key的生成方法
     * 生成格式:className:methodName:parameterType1:...:value:value:..
     * @param lockContext
     * @param keyValueMap
     * @return
     */
    @Override
    protected String doGenerateKey(LockContext lockContext, Map<String, String> keyValueMap) {

        List<String> keyList = Lists.newArrayList();

        keyList.add(lockContext.getClassName());
        keyList.add(lockContext.getMethodName());


        Class[] parameterTypes = lockContext.getParameterTypes();


        if(ArrayUtils.isNotEmpty(parameterTypes)){
            Arrays.stream(parameterTypes).forEach(parameterType->{
                keyList.add(parameterType.toString());
            });

        }else {
            keyList.add(Void.class.toString());
        }



        Collection<String> values = keyValueMap.values();

        if(CollectionUtils.isNotEmpty(values)){
            values.stream().forEach(value->{
                keyList.add(value);
            });

        }


        return keyList.stream().collect(Collectors.joining(","));

    }
}

如果需要更换生成key的规则,只需要实现抽象类,然后注解标注就行

@Lock(keyGenerator = YourCustomKeyGenerator.class)
public void yourMethod() {
    // 方法体
}

最后最后,需要做注解的增强逻辑,定义一个切面类

  1. setApplicationContext(ApplicationContext applicationContext): 实现了 ApplicationContextAware 接口,用于获取 Spring 应用程序上下文对象。在这个方法中,将应用程序上下文对象赋值给类的私有成员变量 applicationContext

  2. lockPointCut(): 定义了一个切入点,用于匹配带有 @Lock 注解的方法。

  3. arroundLock(ProceedingJoinPoint proceedingJoinPoint): 定义了一个环绕通知,用于在带有 @Lock 注解的方法执行前后进行增强处理。在该方法中:

    • 首先,调用 LockContext.init(proceedingJoinPoint) 方法初始化锁上下文信息。
    • 然后,调用 checkAndGetLock(lockContext) 方法检查配置信息并获取锁实体。
    • 接着,尝试获取锁并执行带锁的方法,如果获取锁成功,则调用 proceedingJoinPoint.proceed(args) 方法执行目标方法。
    • 最后,在 finally 块中释放锁。
  4. checkAndGetLock(LockContext lockContext): 检查上下文的配置信息,根据配置信息获取对应的锁实体。在该方法中:

    • 首先,判断 lockRegistry 是否为空,如果为空,则打印错误日志并返回空。
    • 然后,调用 getLockkey(lockContext) 方法获取锁的键。
    • 最后,根据锁的键从 lockRegistry 中获取锁实体并返回。
  5. getLockkey(LockContext lockContext): 获取锁的键。在该方法中:

    • 首先,通过 applicationContext.getBean(lockContext.getAnnotation().keyGenerator()) 获取键生成器实例。
    • 然后,调用键生成器的 generateKey(lockContext) 方法生成锁的键。
    • 如果键生成器实例为空,则打印错误日志并返回空字符串。
@Component
@Aspect
@Slf4j
public class LockAspect implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

      this.applicationContext = applicationContext;

    }


    @Autowired
    private LockRegistry lockRegistry;

    @Pointcut(value = "@annotation(com.yubin.pan.lock.core.annotation.Lock)")
    public void lockPointCut(){

    }

    @Around("lockPointCut()")
    public Object arroundLock(ProceedingJoinPoint proceedingJoinPoint){


        Object result = null;

        LockContext lockContext = LockContext.init(proceedingJoinPoint);

        java.util.concurrent.locks.Lock lock = checkAndGetLock(lockContext);

        if(Objects.isNull(lock)){
            log.error("lock aspect get lock fail.");
            throw new RPanFrameworkException("arroundLock get lock fail");
        }


        boolean lockResult = false;


        try {

         lockResult = lock.tryLock(lockContext.getAnnotation().expireSecond(), TimeUnit.SECONDS);

         if(lockResult){
             Object[] args = proceedingJoinPoint.getArgs();
            result =  proceedingJoinPoint.proceed(args);
         }


        }catch (InterruptedException e){

            log.error("lock aspect tryLock exception..",e);
            throw new RPanFrameworkException("around Lock tryLock fail");

        } catch (Throwable e) {
            log.error("lock aspect tryLock exception..",e);
            throw new RPanFrameworkException("around Lock tryLock fail");
        }finally {
            if(lockResult){
                lock.unlock();
            }
        }

        return result;

    }


    /**
     * 检查上下文的配置信息,返回锁实体
     * @param lockContext
     * @return
     */
    private java.util.concurrent.locks.Lock checkAndGetLock(LockContext lockContext) {

        if(Objects.isNull(lockRegistry)){
            log.error("the lockRegistry is not found...");
            return null;
        }

       String lockKey =  getLockkey(lockContext);


        if(StringUtils.isEmpty(lockKey)){
            return null;
        }

        java.util.concurrent.locks.Lock lock = lockRegistry.obtain(lockKey);

        return lock;
    }


    /**
     * 获取锁key的私有方法
     * @param lockContext
     * @return
     */
    private String getLockkey(LockContext lockContext) {

        KeyGenerator keyGenerator = applicationContext.getBean(lockContext.getAnnotation().keyGenerator());

        if(Objects.nonNull(keyGenerator)){
            return keyGenerator.generateKey(lockContext);
        }

        log.error("the keyGenerator is not found...");

        return StringUtils.EMPTY;
    }

}

测试类,多线程执行,在对应方法上面加注解

  @Test
    public void lockTesterTest() throws InterruptedException{

        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {

            threadPoolTaskExecutor.execute(()->{
                localTester.testLock("imooc");
                countDownLatch.countDown();
            });

        }

           countDownLatch.await();
    }


@Component
public class LocalTester {


    @Lock(name = "test" ,keys = "#name",expireSecond = 10L)
    public String testLock(String name){
        System.out.println(Thread.currentThread().getName() + " get the lock.");
        String result = "hello " + name;
        System.out.println(Thread.currentThread().getName() + " release the lock.");
        return result;
    }
}

结果符合预期

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot provides easy integration with the Spring Integration framework for implementing messaging and integration patterns. Spring Integration provides support for various messaging protocols, including MQTT, a lightweight publish/subscribe messaging protocol. To integrate Spring Integration with MQTT in Spring Boot, follow these steps: 1. Add the following dependencies to your `pom.xml` file: ``` <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-core</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mqtt</artifactId> </dependency> ``` 2. Create a configuration class for MQTT integration: ``` @Configuration @EnableIntegration public class MqttIntegrationConfig { @Value("${mqtt.broker.url}") private String brokerUrl; @Value("${mqtt.client.id}") private String clientId; @Bean public MessageChannel mqttInputChannel() { return new DirectChannel(); } @Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); MqttConnectOptions options = new MqttConnectOptions(); options.setServerURIs(new String[] { brokerUrl }); factory.setConnectionOptions(options); return factory; } @Bean public MessageProducer inbound() { MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( clientId, mqttClientFactory(), "topic1", "topic2"); adapter.setCompletionTimeout(5000); adapter.setConverter(new DefaultPahoMessageConverter()); adapter.setQos(1); return adapter; } @Bean public MessageHandler mqttMessageHandler() { return new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { System.out.println("Received MQTT message: " + message.getPayload()); } }; } @Bean public IntegrationFlow mqttInFlow() { return IntegrationFlows.from(inbound()) .handle(mqttMessageHandler()) .channel(mqttInputChannel()) .get(); } } ``` 3. Configure the MQTT broker URL and client ID in your application properties file: ``` mqtt.broker.url=tcp://localhost:1883 mqtt.client.id=myClientId ``` 4. Use the `mqttInputChannel` channel to send messages to the MQTT broker. For example: ``` @Autowired private MessageChannel mqttInputChannel; public void sendMqttMessage(String payload) { mqttInputChannel.send(MessageBuilder.withPayload(payload).build()); } ``` With these steps, you can easily integrate MQTT messaging with your Spring Boot application using Spring Integration.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值