SpringAOP 常见应用场景

news2025/2/24 13:36:26

文章目录

  • SpringAOP
    • 1 概念
    • 2 常见应用场景
    • 3 AOP的几种通知类型分别有什么常见的应用场景
    • 4 AOP实现 性能监控
      • 4.1 首先,定义一个切面类,用于实现性能监控逻辑:
      • 4.2 定义自定义注解
      • 4.3 注解修饰监控的方法
    • 5 AOP实现 API调用统计
      • 5.1 定义切面类,用于实现API调用统计逻辑
    • 6 AOP实现 缓存
      • 6.1 定义缓存注解
      • 6.2 实现缓存切面
      • 6.3 应用缓存注解
    • 7 AOP实现自定义滑动窗口限流
      • 7.1 定义缓存注解
      • 7.2 滑动窗口限流器
      • 7.3 AOP切面实现
      • 7.4 应用限流注解

SpringAOP

1 概念

Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个模块,它提供了面向切面编程的支持。AOP允许开发者将横切关注点(如日志记录、权限检查、性能监控等)从业务逻辑中分离出来,通过这种方式,可以使代码更加模块化,易于维护和管理。

核心概念:

  1. 切面(Aspect):切面是跨越多个对象的行为或关注点的模块化。例如,事务管理就是企业级应用中的一个关注点,它需要跨越多个对象进行管理。
  2. 连接点(Joinpoint):在程序执行过程中某个特定的点,比如方法的调用或者异常的抛出。在Spring AOP中,连接点总是方法的执行。
  3. 切入点(Pointcut):定义了切面应该在哪些连接点上执行的规则。可以通过表达式来匹配哪些方法需要被切面所影响。
  4. 通知(Advice):在切面识别到某个连接点后要执行的动作。有多种类型的通知,包括前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。
  5. 织入(Weaving):将切面应用到目标对象上,从而创建代理对象的过程。这个过程可以在编译时、类加载时或运行时完成。Spring AOP采用的是运行时织入。

2 常见应用场景

Spring AOP(面向切面编程)的常见应用场景广泛,主要用于将那些遍布于应用各处的横切关注点(Cross-cutting Concerns)进行集中管理和代码复用。以下是一些典型的Spring AOP应用场景:

  1. 日志记录(Logging):在方法调用前后记录日志信息,用于跟踪方法执行情况、性能监控或调试。
  2. 权限检查(Security/Authorization):在方法执行前验证用户是否有权限执行该操作,比如角色检查或资源访问控制。
  3. 事务管理(Transaction Management):自动管理数据库事务的开启、提交或回滚,保证数据的一致性。
  4. 异常处理(Exception Handling):集中处理特定类型的异常,比如记录异常信息或执行特定的恢复操作。
  5. 性能监控(Performance Monitoring):监控方法执行时间,帮助识别和优化性能瓶颈。
  6. 缓存(Caching):自动缓存方法的返回结果,减少不必要的数据库查询或其他耗时操作。
  7. 参数校验和转换(Parameter Validation and Conversion):在方法调用前对参数进行校验或转换,确保符合业务逻辑要求。
  8. API调用统计(API Call Tracking):记录API的调用次数、频率等,用于分析和优化。
  9. SLF4J、Logback、Log4j等日志框架集成:通过AOP可以在不修改业务代码的情况下,灵活地切换或增强日志框架的功能。
  10. 自定义注解的处理:使用AOP拦截带有特定自定义注解的方法,实现特定逻辑,如标记某个方法需要审计、限流等。

这些场景体现了AOP的核心价值,即通过将横切关注点与核心业务逻辑分离,提高代码的可维护性和可读性,同时降低了模块间的耦合度。

aop的几种通知类型分别有什么常见的应用场景

3 AOP的几种通知类型分别有什么常见的应用场景

AOP(面向切面编程)的通知类型是实现切面功能的关键组成部分,每种类型对应着不同的应用场景。以下是Spring AOP五种通知类型及其常见应用场景:

  1. 前置通知(Before advice)
    • 应用场景:在目标方法执行前执行某些操作,如记录方法调用日志、验证权限、参数校验等。例如,在方法开始前记录操作日志,或检查用户是否已经登录。
  2. 后置通知(After returning advice)
    • 应用场景:在目标方法正常执行完毕后执行,通常用于清理资源、记录方法返回值或日志。例如,记录方法执行成功的信息或操作结果,以及进行一些资源清理工作。
  3. 异常通知(After throwing advice)
    • 应用场景:在目标方法抛出异常后执行,用于异常处理,如记录异常日志、发送错误报告、执行异常补偿措施等。例如,捕获并记录业务异常,或者向管理员发送异常报警邮件。
  4. 最终通知(After (finally) advice)
    • 应用场景:无论目标方法是否正常结束(包括正常返回或抛出异常),都会执行的代码块,常用于释放资源、关闭文件或数据库连接等。例如,确保数据库连接关闭,或执行必要的清理操作。
  5. 环绕通知(Around advice)
    • 应用场景:最灵活的通知类型,可以在方法调用前后执行自定义操作,甚至可以选择是否执行目标方法,适用于需要完全控制方法调用流程的场景,如性能监控、事务管理、日志记录与时间度量等。例如,环绕一个方法调用,测量其执行时间的同时控制事务的开启与提交或回滚。

通过这些通知类型,AOP能够有效地将横切关注点(如日志、安全、事务等)从核心业务逻辑中分离出来,提高代码的模块化程度和可维护性。

4 AOP实现 性能监控

在Spring AOP中,实现性能监控的一种常见方法是通过环绕通知(Around Advice)来测量方法的执行时间。环绕通知可以在方法调用前后执行自定义逻辑,非常适合用来监控性能。下面是一个简单的例子,展示如何使用Spring AOP来监控方法的执行时间:

4.1 首先,定义一个切面类,用于实现性能监控逻辑:

这里,我们定义了一个带有@Around注解的环绕通知方法,它会拦截所有标有自定义注解@PerformanceMonitor的方法。PerformanceMonitor是我们自定义的一个注解,用于标记需要监控的方法,并可配置额外的监控阈值。

package com.example.demo.aspect;

import com.example.demo.annotation.PerformanceMonitor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * PerformanceMonitorAspect : 性能监控切面
 *
 * @author zyw
 * @create 2024-06-06  13:18
 */

@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {

    /**
     * 切面方法 Around环绕通知方法
     *
     * @param joinPoint
     * @param performanceMonitor
     * @return
     * @throws Throwable
     */
    @Around("@annotation(performanceMonitor)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            // 执行方法
            Object result = joinPoint.proceed();
            return result;
        } finally {
            long elapsedTime = System.currentTimeMillis() - start;
            // 记录方法执行时间
            log.info("方法: {} 执行了 {} ms", joinPoint.getSignature().getName(), elapsedTime);
            if (performanceMonitor.logIfGreaterThan() > 0 && elapsedTime > performanceMonitor.logIfGreaterThan()) {
                log.warn("性能警告:方法 {} 执行时间超过{}ms.", joinPoint.getSignature().getName(), performanceMonitor.logIfGreaterThan());
            }
        }
    }

}

4.2 定义自定义注解

package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * PerformanceMonitor : 性能监控注解
 *
 * @author zyw
 * @create 2024-06-06  13:19
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PerformanceMonitor {
    // 默认不设置警告阈值
    long logIfGreaterThan() default 0;
}

4.3 注解修饰监控的方法

这样,每次调用performSomeTask方法时,都会自动记录其执行时间,并在超过设定阈值时输出警告信息,帮助识别和优化性能瓶颈。

import com.example.demo.annotation.PerformanceMonitor;    

    @Override
    @PerformanceMonitor(logIfGreaterThan = 100) // 如果执行时间超过100ms,则记录警告日志
    public void performSomeTask(Integer num) {
        if (num == 1){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

在这里插入图片描述

5 AOP实现 API调用统计

5.1 定义切面类,用于实现API调用统计逻辑

在这个切面中,我们使用了ConcurrentHashMapAtomicLong来安全地记录每个API方法的调用次数,确保在高并发环境下也能正确统计。

package com.example.demo.aspect;

import com.example.demo.annotation.PerformanceMonitor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * PerformanceMonitorAspect : 性能监控切面
 *
 * @author zyw
 * @create 2024-06-06  13:18
 */

@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {

    private ConcurrentHashMap<String, AtomicLong> callCountMap = new ConcurrentHashMap<>();
    /**
     * 切面方法 Around环绕通知方法
     *
     * @param joinPoint
     * @param performanceMonitor
     * @return
     * @throws Throwable
     */
    @Around("@annotation(performanceMonitor)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            String methodName = joinPoint.getSignature().getName();
            long count = callCountMap.computeIfAbsent(methodName, k -> new AtomicLong()).incrementAndGet();
            // 执行方法
            Object result = joinPoint.proceed();
            // 可以在这里添加更复杂的统计逻辑,比如记录到数据库、发送到监控系统等
            // 这里简单打印调用次数
            log.info("方法: {} 调用次数: {}", methodName, count);
            return result;
        } finally {
            long elapsedTime = System.currentTimeMillis() - start;
            // 记录方法执行时间
            log.info("方法: {} 执行了 {} ms", joinPoint.getSignature().getName(), elapsedTime);
            if (performanceMonitor.logIfGreaterThan() > 0 && elapsedTime > performanceMonitor.logIfGreaterThan()) {
                log.warn("性能警告:方法 {} 执行时间超过{}ms.", joinPoint.getSignature().getName(), performanceMonitor.logIfGreaterThan());
            }
        }
    }

}

现在,每当/api/data这个API被调用时,ApiCallStatsAspect就会自动增加调用计数,并打印调用次数。你可以根据需要进一步扩展此逻辑,比如定期将统计数据发送到监控系统、数据库等,以便进行更深入的分析和优化。

在这里插入图片描述

6 AOP实现 缓存

6.1 定义缓存注解

首先,你需要定义一个自定义注解,用于标记需要缓存的方法。

package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Cacheable : AOP缓存注解
 *
 * @author zyw
 * @create 2024-06-06  16:16
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cacheable {
    // 缓存的键,可以根据方法参数生成
    String key();
    // 缓存过期时间,默认永不过期
    long ttl() default 0;
}

6.2 实现缓存切面

接下来,创建一个切面来拦截带有@Cacheable注解的方法,并实现缓存逻辑。

package com.example.demo.aspect;

import com.example.demo.annotation.Cacheable;
import com.example.demo.uitls.RedisUtil;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


/**
 * CacheAspect :
 *
 * @author zyw
 * @create 2024-06-06  16:17
 */

@Aspect
@Component
public class CacheAspect {

    @Resource
    private RedisUtil redisUtil;

    @Around("@annotation(cacheable)")
    public Object cacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String cacheKey = generateCacheKey(joinPoint, cacheable.key());
        Object result = redisUtil.get(cacheKey);

        if (result == null) {
            // 缓存中没有,执行方法并缓存结果
            result = joinPoint.proceed();
            if (cacheable.ttl() > 0) {
                redisUtil.set(cacheKey, result, cacheable.ttl());
            } else {
                redisUtil.set(cacheKey, result);
            }
        }

        return result;
    }

    private String generateCacheKey(ProceedingJoinPoint joinPoint, String keyExpression) {
        // 根据方法名、参数等生成缓存键,这里简化处理,实际可能需要更复杂的逻辑
        StringBuilder keyBuilder = new StringBuilder(keyExpression);
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            keyBuilder.append(":").append(arg.toString());
        }
        return keyBuilder.toString();
    }

}

6.3 应用缓存注解

最后,在你想要缓存其返回结果的方法上使用@Cacheable注解。

package com.example.demo.service.impl;

import com.example.demo.annotation.Cacheable;
import com.example.demo.annotation.PerformanceMonitor;
import com.example.demo.entity.SysUser;
import com.example.demo.service.SysUserService;
import com.example.demo.service.TestService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;

/**
 * TestServiceImpl :
 *
 * @author zyw
 * @create 2023-12-18  15:15
 */
@Service
public class TestServiceImpl implements TestService {

    @Resource
    private SysUserService sysUserService;

    @Override
    @Cacheable(key = "UserListCacheKey", ttl = 60) // 缓存1分钟
    public List<SysUser> getUserList() {
        return sysUserService.list();
    }
}

可用于结果固定,频繁需要获取的数据集,首次查询时走数据库,后缓存有效期内再次获取都从redis中取

在这里插入图片描述
在这里插入图片描述

7 AOP实现自定义滑动窗口限流

要实现AOP结合滑动窗口算法来实现自定义规则的限流,我们可以在原有的基础上进一步扩展,以支持更灵活的配置和更复杂的规则。以下是一个基于Spring AOP和滑动窗口算法的简单示例,包括自定义注解来设置限流规则,以及如何在切面中应用这些规则。

7.1 定义缓存注解

首先,定义一个自定义注解来标记需要限流的方法,并允许传入限流的具体规则

package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * SlidingWindowRateLimiter : 滑动窗口限流注解
 *
 * @author zyw
 * @create 2024-06-06  17:20
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WindowRateLimit {
    // 允许的最大请求数
    int limit();
    // 窗口时间长度,单位毫秒
    long timeWindowMilliseconds();
}

7.2 滑动窗口限流器

接下来,实现滑动窗口限流器,这里简化处理,直接使用内存实现,实际应用中可能需要基于Redis等持久化存储以适应分布式场景:

package com.example.demo.uitls;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.LinkedList;

/**
 * SlidingWindowRateLimiter : 滑动窗口限流算法
 *
 * @author zyw
 * @create 2024-06-07  15:16
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SlidingWindowRateLimiter implements Serializable {
    /**
     * 请求队列
     */
    private LinkedList<Long> requests = new LinkedList<>();
    /**
     * 最大请求数
     */
    private int maxRequests;
    /**
     * 窗口大小
     */
    private long windowSizeInMilliseconds;

    public SlidingWindowRateLimiter(int maxRequests, long windowSizeInMilliseconds) {
        this.maxRequests = maxRequests;
        this.windowSizeInMilliseconds = windowSizeInMilliseconds;
    }

    /**
     * 判断是否允许请求
     * @return
     */
    public synchronized boolean allowRequest() {
        // 获取当前时间
        long currentTime = System.currentTimeMillis();

        // 清除窗口之外的旧请求
        while (!requests.isEmpty() && currentTime - requests.peekFirst() > windowSizeInMilliseconds) {
            requests.removeFirst();
        }

        // 如果当前窗口请求未达到上限,则允许请求并记录
        if (requests.size() < maxRequests) {
            requests.addLast(currentTime);
            return true;
        } else {
            // 达到限流阈值,拒绝请求
            return false;
        }
    }
}

7.3 AOP切面实现

最后,创建AOP切面来应用限流逻辑:

package com.example.demo.aspect;

import com.example.demo.annotation.WindowRateLimit;
import com.example.demo.config.redis.RedisKeyEnum;
import com.example.demo.uitls.RedisUtil;
import com.example.demo.uitls.SlidingWindowRateLimiter;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * RateLimiterAspect :
 *
 * @author zyw
 * @create 2024-06-06  17:21
 */

@Aspect
@Component
public class SlidingWindowRateLimiterAspect {

    @Resource
    private RedisUtil redisUtil;

    @Around("@annotation(rateLimit)")
    public Object applyRateLimit(ProceedingJoinPoint joinPoint, WindowRateLimit rateLimit) throws Throwable {
        // 获取调用的方法名
        String methodName = joinPoint.getSignature().getName();
        // 获取方法对应的缓存滑动窗口限流器KEY
        String key = RedisKeyEnum.WINDOW_CURRENT_LIMITING.getKey() + methodName;
        // 从缓存中获取滑动窗口限流器
        SlidingWindowRateLimiter rateLimiter = redisUtil.getCacheObject(key);
        // 如果滑动窗口限流器不存在,则创建一个新限流器
        if (rateLimiter == null) {
            rateLimiter = new SlidingWindowRateLimiter(rateLimit.limit(), rateLimit.timeWindowMilliseconds());
        }
        // 如果滑动窗口限流器存在,则判断是否允许请求
        if (!rateLimiter.allowRequest()) {
            throw new RuntimeException("Too many requests, please try again later.");
        }
        // 如果允许请求,则更新滑动窗口限流器
        redisUtil.setCacheObject(key, rateLimiter);
        // 允许执行方法
        return joinPoint.proceed(); 
    }

}

7.4 应用限流注解

在需要做限流的方法上加上注解,在注解参数中设定 允许的最大请求数窗口时间长度(单位毫秒)

package com.example.demo.service.impl;

import com.example.demo.annotation.WindowRateLimit;
import com.example.demo.service.TestService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

/**
 * TestServiceImpl :
 *
 * @author zyw
 * @create 2023-12-18  15:15
 */
@Service
public class TestServiceImpl implements TestService {

    @Override
    @WindowRateLimit(limit = 5, timeWindowMilliseconds = 60L*1000) // 每最多允许5次请求
    public String getContent() {
        return "Hello Word";
    }
}

首次请求时,初始化滑动窗口限流器,记录第一次请求的时间戳

在这里插入图片描述

窗口期内,记录了五次请求的时间戳后,已达到我们在注解中设置的窗口期最大请求量

在这里插入图片描述

此时接口限流

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1801786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024年AI大模型训练数据白皮书作用

2024年AI大模型训练数据白皮书 在人工智能迅猛发展的今天&#xff0c;AI大模型的训练数据质量和管理成为影响其性能和应用效果的关键因素。《2024年AI大模型训练数据白皮书》为业内人士提供了一份详尽的指南&#xff0c;揭示了当前AI大模型训练数据的最新趋势、最佳实践以及未…

通州公司代理记账,以专业服务提升企业运营效率

我们为您提供“通州公司代理记账”的全面信息&#xff0c;作为一家专业的代理记账机构&#xff0c;我们的目标是为您的企业提供最优质的服务&#xff0c;帮助您更高效地管理财务。 代理记账是一项重要的商业活动&#xff0c;它不仅可以帮助企业节约时间和成本&#xff0c;而且…

逻辑这回事(三)----时序分析与时序优化

基本时序参数 图1.1 D触发器结构 图1.2 D触发器时序 时钟clk采样数据D时&#xff0c;Tsu表示数据前边沿距离时钟上升沿的时间&#xff0c;MicTsu表示时钟clk能够稳定采样数据D的所要求时间&#xff0c;Th表示数据后边沿距离时钟上升沿的时间&#xff0c;MicTh表示时钟clk采样…

使用 Elasticsearch 调用 OpenAI 函数

作者&#xff1a;来自 Elastic Ashish Tiwari 介绍 OpenAI 中的函数调用是指 AI 模型与外部函数或 API 交互的能力&#xff0c;使它们能够执行文本生成之外的任务。此功能使模型能够通过调用预定义函数来执行代码、从数据库检索信息、与外部服务交互等。 该模型根据用户提示智…

【核心动画-转场动画-CATransition Objective-C语言】

一、转场动画,CATransition, 1.接下来,我们来说这个转场动画啊,效果呢,会做这么一个小例子, 感觉有一个3D的一个样式一样, 转场动画呢,就是说,你在同一个View,比如说,imageView,去切换图片的时候,你可以去用这个,转场动画, 实际上,包括,控制器之间的切换,也…

【WEB系列】过滤器Filter

Filter&#xff0c;过滤器&#xff0c;属于Servlet规范&#xff0c;并不是Spring独有的。其作用从命名上也可以看出一二&#xff0c;拦截一个请求&#xff0c;做一些业务逻辑操作&#xff0c;然后可以决定请求是否可以继续往下分发&#xff0c;落到其他的Filter或者对应的Servl…

微服务架构-正向治理与治理效果

目录 一、正向治理 1.1 概述 1.2 效率治理 1.2.1 概述 1.2.2 基于流量录制和回放的测试 1.2.3 基于仿真环境的测试 1.3 稳定性治理 1.3.1 概述 1.3.2 稳定性治理模型 1.3.3 基于容器化的稳定性治理 1.3.3.1 概述 1.3.3.2 测试 1.3.3.3 部署 1.3.3.3.1 概述 1.3.3…

零基础入门学用Arduino 第一部分(三)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

添加west扩展命令

使用west工具的帮助命令&#xff0c;west -h&#xff0c;不仅可以列出west工具的内置命令&#xff0c;也可以列举当前工程中实现的扩展命令&#xff0c;如build&#xff0c;flash等。 本文将介绍如何添加扩展命令。 west扩展命令的位置通过以下方式查找&#xff1a; 1. 首先找…

网络编程之XDP技术介绍

一、简介 XDP&#xff1a;eXpress Data Path&#xff0c;快速数据面&#xff0c;听名字是不是很高大上。其实它就是一个快速处理Rx数据包的数据面技术。为什么现在对数据处理如此敏感&#xff1f;原因非常简单&#xff0c;随着网络的不断覆盖社会的各个层面&#xff0c;海量的…

对比深度图聚类的硬样本感知网络

Hard Sample Aware Network for Contrastive Deep Graph Clustering 文章目录 Hard Sample Aware Network for Contrastive Deep Graph Clustering摘要引言方法实验结论启发点 摘要 本文提出了一种名为Hard Sample Aware Network (HSAN)的新方法&#xff0c;用于对比深度图聚类…

AI智能客服经验总结

AI智能客服经验总结 在使用OpenAI的API接口来构建人工智能客服系统时&#xff0c;prompt约束是非常重要的。它可以帮助确保生成的回复符合你的期望和需求。以下是一些关于如何设置和管理prompt约束的建议&#xff1a; 1. 明确客服系统的目标 首先&#xff0c;明确你的客服系…

大泽动力30KW静音汽油发电机

安全操作&#xff1a; 在使用前&#xff0c;确保发电机放置在通风良好、干燥、无易燃物品的地方。 避免在发电机运行时触摸其热表面或运转部件&#xff0c;以免烫伤或受伤。 遵循发电机的启动和停机程序&#xff0c;不要随意操作。 燃油管理&#xff1a; 使用高质量的汽油&…

柴油十六烷值检测 液压油硫含量检测 变压器油检测

检测的油品包括&#xff1a;柴油、润滑油、液压油、机油、汽油、以及一些工业用油等。 柴油检测项目GB19147-2013&#xff1a;氧化安定性、硫含量、酸度、10%蒸余物残炭、灰分、铜片腐蚀、水分、机械杂质、润滑性、多环芳烃、运动粘度、凝点、冷滤点、闪点、十六烷值、馏程、密…

pdf压缩文件怎么压缩最小,软件工具压缩清晰

PDF格式的文件&#xff0c;当其体积过于庞大时&#xff0c;确实在上传的过程中显得尤为不便。今天给大家分享一个压缩pdf的简单的方法&#xff0c;让大家可以轻松的压缩pdf。 浏览器打开 "轻云处理pdf官网" &#xff0c;上传pdf文件&#xff0c;文件上传完成后网站会…

汇编:头文件

汇编头文件&#xff08;header files&#xff09;在汇编语言编程中类似于高层语言中的头文件&#xff0c;它们通常包含宏定义、常量定义、数据结构定义、函数声明以及其他在多个汇编源文件中共享的代码&#xff1b;使用头文件可以提高代码的可维护性和可读性&#xff0c;并使代…

3~35kV户内开关柜综合测控装置

安科瑞电气股份有限公司 祁洁 15000363176 一、产品概述 ASD系列开关柜综合测控装置用于3~35kV户内开关柜&#xff0c;适用于中置柜、手车柜、固定柜、环网柜等多种开关柜。具有一次回路模拟图及开关状态指示&#xff0c;高压带电显示及核相&#xff0c;自动温湿度控制&…

炫技来了!使用SDR设备成功抓到蓝牙air packet, 并且wireshark实时解析, 没错就是蓝牙空口抓包器

本文章主要介绍是用ZYNQ7020AD9361Gnu radio是搭建一个蓝牙抓包器的文章。 由于之前一直做蓝牙Host&#xff0c;对controller觉得是一个比较虚无缥缈的东西&#xff0c;得不到的总是在骚动&#xff0c;所以最近用我用吃灰了2年的SDR(Software Defined Radio&#xff09;设备研…

【Java基础】OkHttp 超时设置详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

win10重装系统?电脑系统重装一键清晰,干货分享!

在电脑的使用过程中&#xff0c;由于各种原因&#xff0c;我们可能会遇到系统崩溃、运行缓慢或者出现各种难以解决的问题。这时&#xff0c;重装系统往往是一个有效的解决方案。今天&#xff0c;我们就来详细介绍一下如何在Win10环境下进行系统的重装&#xff0c;帮助大家轻松解…