SpringAOP 常见应用场景
文章目录
- 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允许开发者将横切关注点(如日志记录、权限检查、性能监控等)从业务逻辑中分离出来,通过这种方式,可以使代码更加模块化,易于维护和管理。
核心概念:
- 切面(Aspect):切面是跨越多个对象的行为或关注点的模块化。例如,事务管理就是企业级应用中的一个关注点,它需要跨越多个对象进行管理。
- 连接点(Joinpoint):在程序执行过程中某个特定的点,比如方法的调用或者异常的抛出。在Spring AOP中,连接点总是方法的执行。
- 切入点(Pointcut):定义了切面应该在哪些连接点上执行的规则。可以通过表达式来匹配哪些方法需要被切面所影响。
- 通知(Advice):在切面识别到某个连接点后要执行的动作。有多种类型的通知,包括前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。
- 织入(Weaving):将切面应用到目标对象上,从而创建代理对象的过程。这个过程可以在编译时、类加载时或运行时完成。Spring AOP采用的是运行时织入。
2 常见应用场景
Spring AOP(面向切面编程)的常见应用场景广泛,主要用于将那些遍布于应用各处的横切关注点(Cross-cutting Concerns)进行集中管理和代码复用。以下是一些典型的Spring AOP应用场景:
- 日志记录(Logging):在方法调用前后记录日志信息,用于跟踪方法执行情况、性能监控或调试。
- 权限检查(Security/Authorization):在方法执行前验证用户是否有权限执行该操作,比如角色检查或资源访问控制。
- 事务管理(Transaction Management):自动管理数据库事务的开启、提交或回滚,保证数据的一致性。
- 异常处理(Exception Handling):集中处理特定类型的异常,比如记录异常信息或执行特定的恢复操作。
- 性能监控(Performance Monitoring):监控方法执行时间,帮助识别和优化性能瓶颈。
- 缓存(Caching):自动缓存方法的返回结果,减少不必要的数据库查询或其他耗时操作。
- 参数校验和转换(Parameter Validation and Conversion):在方法调用前对参数进行校验或转换,确保符合业务逻辑要求。
- API调用统计(API Call Tracking):记录API的调用次数、频率等,用于分析和优化。
- SLF4J、Logback、Log4j等日志框架集成:通过AOP可以在不修改业务代码的情况下,灵活地切换或增强日志框架的功能。
- 自定义注解的处理:使用AOP拦截带有特定自定义注解的方法,实现特定逻辑,如标记某个方法需要审计、限流等。
这些场景体现了AOP的核心价值,即通过将横切关注点与核心业务逻辑分离,提高代码的可维护性和可读性,同时降低了模块间的耦合度。
aop的几种通知类型分别有什么常见的应用场景
3 AOP的几种通知类型分别有什么常见的应用场景
AOP(面向切面编程)的通知类型是实现切面功能的关键组成部分,每种类型对应着不同的应用场景。以下是Spring AOP五种通知类型及其常见应用场景:
- 前置通知(Before advice)
- 应用场景:在目标方法执行前执行某些操作,如记录方法调用日志、验证权限、参数校验等。例如,在方法开始前记录操作日志,或检查用户是否已经登录。
- 后置通知(After returning advice)
- 应用场景:在目标方法正常执行完毕后执行,通常用于清理资源、记录方法返回值或日志。例如,记录方法执行成功的信息或操作结果,以及进行一些资源清理工作。
- 异常通知(After throwing advice)
- 应用场景:在目标方法抛出异常后执行,用于异常处理,如记录异常日志、发送错误报告、执行异常补偿措施等。例如,捕获并记录业务异常,或者向管理员发送异常报警邮件。
- 最终通知(After (finally) advice)
- 应用场景:无论目标方法是否正常结束(包括正常返回或抛出异常),都会执行的代码块,常用于释放资源、关闭文件或数据库连接等。例如,确保数据库连接关闭,或执行必要的清理操作。
- 环绕通知(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调用统计逻辑
在这个切面中,我们使用了ConcurrentHashMap
和AtomicLong
来安全地记录每个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 {@Resourceprivate 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 {@Resourceprivate 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 {@Resourceprivate RedisUtil redisUtil;@Around("@annotation(rateLimit)")public Object applyRateLimit(ProceedingJoinPoint joinPoint, WindowRateLimit rateLimit) throws Throwable {// 获取调用的方法名String methodName = joinPoint.getSignature().getName();// 获取方法对应的缓存滑动窗口限流器KEYString 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";}
}
首次请求时,初始化滑动窗口限流器,记录第一次请求的时间戳
窗口期内,记录了五次请求的时间戳后,已达到我们在注解中设置的窗口期最大请求量
此时接口限流
相关文章:
SpringAOP 常见应用场景
文章目录 SpringAOP1 概念2 常见应用场景3 AOP的几种通知类型分别有什么常见的应用场景4 AOP实现 性能监控4.1 首先,定义一个切面类,用于实现性能监控逻辑:4.2 定义自定义注解4.3 注解修饰监控的方法 5 AOP实现 API调用统计5.1 定义切面类&am…...
html+css示例
HTML HTML(超文本标记语言)和CSS(层叠样式表)是构建和设计网页的两种主要技术。HTML用于创建网页的结构和内容,而CSS用于控制其外观和布局。 HTML基础 HTML使用标签来标记网页中的不同部分。每个标签通常有一个开始…...
Day51 动态规划part10+Day52 动态规划part11
LC121买卖股票的最佳时机(未掌握) 暴力:双层循环寻找最优间距,每一次都确定一个起点,遍历剩余节点当作终点 贪心:取最左最小值,不断遍历那么得到的差值最最大值就是最大利润。 动态规划 dp数组…...
Wireshark自定义Lua插件
背景: 常见的抓包工具有tcpdump和wireshark,二者可基于网卡进行抓包:tcpdump用于Linux环境抓包,而wireshark用于windows环境。抓包后需借助包分析工具对数据进行解析,将不可读的二进制数转换为可读的数据结构。 wires…...
商城项目【尚品汇】07分布式锁-2 Redisson篇
文章目录 1 Redisson功能介绍2 Redisson在Springboot中快速入门(代码)2.1 导入依赖2.2 Redisson配置2.3 将自定义锁setnx换成Redisson实现(可重入锁) 3 可重入锁原理3.1 自定义分布式锁setnx为什么不可以重入3.2 redisson为什么可…...
Adobe Illustrator 矢量图设计软件下载安装,Illustrator 轻松创建各种矢量图形
Adobe Illustrator,它不仅仅是一个简单的图形编辑工具,更是一个拥有丰富功能和强大性能的设计利器。 在这款软件中,用户可以通过各种精心设计的工具,轻松创建和编辑基于矢量路径的图形文件。这些矢量图形不仅具有高度的可编辑性&a…...
Nvidia/算能 +FPGA+AI大算力边缘计算盒子:中国舰船研究院
中国舰船研究院又称中国船舶重工集团公司第七研究院,隶属于中国船舶重工集团公司,是专门从事舰船研究、设计、开发的科学技术研究机构,是中国船舶重工集团公司的军品技术研究中心、科技开发中心;主要从事舰船武器装备发展战略研究…...
双网卡配置IP和路由总结
1.在网络适配器属性IPv4中设置默认网关(记网关地址为A),将会在本地路由表中新增一条记录: 网络号子网掩码网关地址0.0.0.00.0.0.0A 2.如果有两个网卡(假设一个连接内网,一个连接互联网)&#…...
【纯血鸿蒙】——自适应布局如何实现?
界面级一多能力有 2 类: 自适应布局: 略微调整界面结构 响应式布局:比较大的界面调整 本文章先主要讲解自适应布局,响应式布局再后面文章再细讲。话不多说,开始了。 自适应布局 针对常见的开发场景,方舟开发框架提…...
Qt5学习笔记(一):Qt Widgets Application项目初探
笔者长期使用MFC开发Windows GUI软件。随着软件向Linux平台迁移的趋势越发明朗,GUI程序的跨平台需求也越来越多。因此笔者计划重新抓一下Qt来实现跨平台GUI程序的实现。 0x01. 看看Qt Widgets Application项目结构 打开Qt5,点击“ New”按钮新建项目。…...
Linux网络编程:数据链路层协议
目录 前言: 1.以太网 1.1.以太网帧格式 1.2.MTU(最大传输单元) 1.2.1.IP协议和MTU 1.2.2.UDP协议和MTU 1.2.3.TCP协议和MTU 2.ARP协议(地址解析协议) 2.1.ARP在局域网通信的角色 2.2.ARP报文格式 2.3.ARP报文…...
企业估值的三种方法
估值模型三剑客—DCF、P/E、EV /EBITDA 三种主要估值模型的优缺点: DCF 优点:通过对自由现金流的折现计算,反映了公司内在价值的本质,是最重要与最合理的估值方法。 缺点:未来自由现金流的估计不准确,受折现率影响…...
比亚迪正式签约国际皮划艇联合会和中国皮划艇协会,助推龙舟入奥新阶段
6月5日,比亚迪与国际皮划艇联合会、中国皮划艇协会在深圳共同签署合作协议,国际皮划艇联合会主席托马斯科涅茨科,国际皮划艇联合会秘书长理查德派蒂特,中国皮划艇协会秘书长张茵,比亚迪品牌及公关处总经理李云飞&#…...
宏集Panorama SCADA:个性化定制,满足多元角色需求
前言 在考虑不同人员在企业中的职能和职责时,他们对于SCADA系统的需求可能因其角色和工作职责的不同而有所差异。在SCADA系统的设计和实施过程中,必须充分考虑和解决这种差异性。 为了满足不同人员的需求, 宏集Panorama SCADA平台具备灵活的功能和定制…...
聪明人社交的基本顺序:千万别搞反了,越早明白越好
聪明人社交的基本顺序:千万别搞反了,越早明白越好 国学文化 德鲁克博雅管理 2024-03-27 17:00 作者:方小格 来源:国学文化(gxwh001) 导语 比一个好的圈子更重要的,是自己优质的能力。 唐诗宋…...
图片和PDF展示预览、并支持下载
需求 展示图片和PDF类型,并且点击图片或者PDF可以预览 第一步:遍历所有的图片和PDF列表 <div v-for"(data,index) in parerFont(item.fileInfo)" :key"index" class"data-list-item"><downloadCard :file-inf…...
图论第5天
127.单词接龙 需要cout看一下过程。 #include <iostream> #include <queue> #include <stack> #include <unordered_map> #include <unordered_set> #include <vector> using namespace ::std;class Solution { public:int ladderLength(…...
Java开发-面试题-0004-HashMap 和 Hashtable的区别
Java开发-面试题-0004-HashMap 和 Hashtable的区别 更多内容欢迎关注我(持续更新中,欢迎Star✨) Github:CodeZeng1998/Java-Developer-Work-Note 技术公众号:CodeZeng1998(纯纯技术文) 生活…...
Swift 序列(Sequence)排序面面俱到 - 从过去到现在(一)
概览 在任何语言中对序列(或集合)元素的排序无疑是一种司空见惯的常规操作,在 Swift 语言里自然也不例外。序列排序看似简单,实则“暗藏玄机”。 要想真正掌握 Swift 语言中对排序的“各种姿势”,我们还得从长计议。不如就先从最简单的排序基本功开始聊起吧。 在本篇博…...
redis 04 redis结构
1.客户端...
【原创】springboot+mysql农业园区管理系统设计与实现
个人主页:程序猿小小杨 个人简介:从事开发多年,Java、Php、Python、前端开发均有涉猎 博客内容:Java项目实战、项目演示、技术分享 文末有作者名片,希望和大家一起共同进步,你只管努力,剩下的交…...
web前端 孙俏:深度探索与实战之路
web前端 孙俏:深度探索与实战之路 在这个数字化、信息化的时代,web前端技术以其独特的魅力,吸引着越来越多的开发者投身其中。今天,我们将跟随孙俏的脚步,一同探索web前端的深度与广度,揭开其神秘的面纱。…...
opencv实战小结-银行卡号识别
实战1-银行卡号识别 项目来源:opencv入门 项目目的:识别传入的银行卡照片中的卡号 难点:银行卡上会有一些干扰项,如何排除这些干扰项,并且打印正确的号码是一个问题 最终效果如上图 实现这样的功能需要以下几个步骤…...
Windows API 开发桌面应用程序,在窗口按下鼠标左键不放可以拖图,并且拖图期间鼠标图标变成手掌
在Windows API中,要实现鼠标左键按下并拖动以移动窗口中的某个图形,并且同时改变鼠标图标为“手掌”形状(这通常指的是“拖动”或“移动”的图标),你需要执行几个步骤。 以下是一个基本的步骤指南,用于在W…...
Docker的网络管理
文章目录 一、Docker容器之间的通信1、直接互联(默认Bridge网络)1.1、Docker安装后默认的网络配置1.2、创建容器后的网络配置1.2.1、首先创建一个容器1.2.2、ip a 列出网卡变化信息1.2.3、查看新建容器后的桥接状态 1.3、容器内安装常见的工具1.4、容器间…...
【数据结构】平衡二叉树左旋右旋与红黑树
平衡二叉树左旋右旋与红黑树 平衡二叉树 定义 平衡二叉树是二叉搜索树的一种特殊形式。二叉搜索树(Binary Search Tree,BST)是一种具有以下性质的二叉树: 对于树中的每个节点,其左子树中的所有节点都小于该节点的值…...
2024蓝桥杯初赛决赛pwn题全解
蓝桥杯初赛决赛pwn题解 初赛第一题第二题 决赛getting_startedbabyheap 初赛 第一题 有system函数,并且能在bss上读入字符 而且存在栈溢出,只要过掉check函数即可 check函数中,主要是对system常规获取权限的参数,进行了过滤&…...
大模型多轮问答的两种方式
前言 大模型的多轮问答难点就是在于如何精确识别用户最新的提问的真实意图,而在常见的使用大模型进行多轮对话方式中,我接触到的只有两种方式: 一种是简单地直接使用 user 和 assistant 两个角色将一问一答的会话内容喂给大模型,…...
【无标题】1877A
足球锦标赛中有 n支球队。每对队伍匹配一次。每场比赛结束后,Pak Chanek收到两个整数作为比赛结果,即两队在比赛中得分的数量。一支球队的效率等于本队每场比赛的总进球数减去对手每场比赛的总进球数。 比赛结束后,Pak Dengklek会计算每支球…...
直播美颜工具解析:美颜SDK核心技术与性能优化方法
本篇文章,小编将深入解析直播美颜SDK的核心技术及其性能优化方法,以期为开发者提供有价值的参考。 一、美颜SDK核心技术 1.实时人脸检测与识别 美颜SDK的核心技术之一是实时人脸检测与识别。这项技术基于深度学习算法,能够快速、准确地识别…...
宜春网站制作/网络推广赚钱平台有哪些
工控机安装 openvino2021.4 需要安装python 就安装了python3.8.8 但是直接报错 安装不上去 在网上找了各种方法,最后安装了KB2533623 之后可以安装python了 下载地址 链接: https://pan.baidu.com/s/15KpcRN2w5v7xQtaFm7JlMw?pwdaxs6 提取码: axs6 或者 KB25…...
ppt做的比较好的网站/网页制作的软件
我们常见的执行js代码都是放入到HTML引入后然后通过HTML文件来执行胡查看代码,显然这是比较麻烦的事情, 如果你的电脑里面安装了node.js,你可以使用node来直接使用node来运行你想要运行的js文件, 具体的操作如图所示:…...
成都装饰公司网站建设/百度seo规则
(一)String类: Java使用String类来创建和操作字符串;String str"hello World"; 注意:String类是不可变的,一旦创建了String对象,他的值就无法改变了; 常用方法…...
临沂网站建设哪家更好/自己想做个网站怎么做
在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。本文为大家介绍java比较器的使用。java比较器的使用对于一个实现了Comparable接口的对象,该接口只能实现一次。如果在一个集合里面需要使用id排序,而在另外一个集合…...
邮件格式模板/太原seo快速排名
原标题:能测血压,还有心电图,兼顾9大运动模式,dido手环还不错随着科技的不断进步,高科技产品不断的涌向市场,无论是从0到1的新概念产品,还是科技互联网赋能的加持产品,都得到了快速的…...
建设网站需要钱吗/seo渠道是什么意思
文档就绪函数这些是通常在jQuery中使用的不同类型的Document Ready函数 (又名jQuery DOM Ready)。 许多开发人员似乎在不知道为什么的情况下使用它们。 因此,我将尝试解释为什么您可能会选择一个版本而不是另一个版本。 可以将文档就绪功能看…...