Spring - 13 ( 11000 字 Spring 入门级教程 )
一: Spring AOP
备注:之前学习 Spring 学到 AOP 就去梳理之前学习的知识点了,后面因为各种原因导致 Spring AOP 的博客一直搁置。。。。。。下面开始正式的讲解。
学习完 Spring 的统一功能后,我们就进入了 Spring AOP 的学习。Spring 的第一大核心是 IoC,而 AOP 则是 Spring 框架的第二大核心,意思是面向切面编程。AOP 的核心思想是针对特定问题进行集中处理,例如我们之前的 “登录校验” 就是一个典型的特定问题,我们当时是通过校验拦截器实现这一问题的统一处理。因此,拦截器可以被视为 AOP 思想的一种具体应用。 AOP 作为一种思想,为开发提供了结构化的方式,而拦截器则是实现这一思想的重要工具。
1.1 AOP 概述
什么是 Spring AOP?AOP 是一种思想,其实现方式有很多,包括 Spring AOP、AspectJ、CGLIB 等。其中 Spring AOP 是众多实现方式之一。学习了 Spring 的一些统一功能后,很多人可能会误认为这就完全掌握了 Spring AOP,实际上并非如此。AOP 的作用维度更为细致,能够根据包、类、方法名和参数等进行拦截,以实现更加复杂的业务逻辑。
例如,在一个项目中,我们开发了多个业务功能,但发现某些业务处理的执行效率较低,需要进行优化。第一步是定位执行耗时较长的业务方法,然后针对这些方法进行优化。为了实现这一点,我们需要统计每个业务方法的执行时间。我们可以在业务方法运行前后记录开始时间和结束时间,计算得出方法的耗时。
虽然这个方法在理论上可行,但在实际开发中,每个业务模块可能包含多个接口,每个接口又有许多方法。如果要在每个方法中都添加耗时统计代码,将增加开发人员的工作量。而 AOP 可以在不修改原始代码的情况下,对特定方法进行功能增强,实现无侵入性设计,降低代码耦合度。接下来,我们将深入了解 Spring AOP 如何实现这一点。
1.2 Spring AOP 快速入门
首先需要在 pom.xml 中引入 AOP 依赖才能使用 AOP 的相关功能:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
导入依赖后开始编写 AOP 程序:
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;@Slf4j
// 声明当前类为切面类。
@Aspect
@Component
public class TimeAspect {/*** 记录方法耗时*/// 定义环绕通知,拦截目标方法并在其前后执行自定义逻辑。// execution:匹配方法执行的连接点。*:通配符,匹配任意返回类型、方法名、参数。// com.example.demo.controller.*.*(..):拦截 controller 包下所有类的所有方法,参数不限。// ProceedingJoinPoint pjp 封装了被拦截的目标方法信息(如方法名、参数、目标对象等)。@Around("execution(* com.example.demo.controller.*.*(..))")public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {// 记录方法执行开始时间long begin = System.currentTimeMillis();// 执行原始方法,pjp.proceed() 调用被拦截的目标方法,这是环绕通知中必须执行的步骤,否则目标方法不会运行。Object result = pjp.proceed();// 记录方法执行结束时间long end = System.currentTimeMillis();// 记录方法执行耗时log.info(pjp.getSignature() + " 执行耗时: {}ms", end - begin);return result;}
}
1.3 Spring AOP 详解
1.3.1 切点
切点也称为 “切入点”,切点用于定义一组规则,告知程序需要对哪些方法进行功能增强,指定作用域和作用方式
1.3.2 连接点
连接点是指满足切点表达式规则的方法,它代表了可以被 AOP 控制的具体方法。连接点与切点之间的关系是:连接点是符合切点表达式的特定元素,而切点则可以看作是包含多个连接点的集合。例如,全体教师可以看作是一个切点,而张三老师或者李四老师就可以看作是一个连接点。
1.3.3 通知
通知是指具体要执行的工作,通常指那些重复的逻辑或共性功能,最终体现为一个方法。在 AOP 中,我们将这部分重复的代码逻辑进行抽取并单独定义,这些抽取出的代码便构成了通知的内容。
1.3.4 切面
切面是切点与通知的结合,通过切面可以描述 AOP 程序在针对哪些方法时,何时执行什么样的操作。切面不仅包含通知逻辑的定义,还包括连接点的定义。通常,将包含切面的类称为切面类,这些类通过 @Aspect 注解进行标识。
1.4 通知类型
Spring 中 AOP 的通知类型有以下几种:
注解 | 描述 |
---|---|
@Around | 环绕通知,标注的方法在目标方法前后都会被执行。 |
@Before | 前置通知,标注的方法在目标方法之前执行。 |
@AfterReturning | 返回后通知,标注的方法在目标方法正常返回后执行,异常时不会执行。 |
@After | 后置通知,标注的方法在目标方法之后执行,无论是否有异常都会执行。 |
@AfterThrowing | 异常后通知,标注的方法在目标方法抛出异常后执行。 |
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo {// 前置通知@Before("execution(* com.example.demo.controller.*.*(..))")public void doBefore() {log.info("执行 Before 方法");}// 后置通知@After("execution(* com.example.demo.controller.*.*(..))")public void doAfter() {log.info("执行 After 方法");}// 返回后通知@AfterReturning("execution(* com.example.demo.controller.*.*(..))")public void doAfterReturning() {log.info("执行 AfterReturning 方法");}// 抛出异常后通知@AfterThrowing("execution(* com.example.demo.controller.*.*(..))")public void doAfterThrowing() {log.info("执行 doAfterThrowing 方法");}// 环绕通知@Around("execution(* com.example.demo.controller.*.*(..))")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around 方法开始执行");Object result = joinPoint.proceed();log.info("Around 方法结束执行");return result;}
}
package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String t1() {return "t1";}@RequestMapping("/t2")public boolean t2() {int a = 10 / 0;return true;}
}
- 访问 http://127.0.0.1:8080/test/t1,此时是正常的运行情况:
- 访问 http://127.0.0.1:8080/test/t2,此时是异常的运行情况:
此处需要注意 @AfterReturning 方法不会被执行,而 @AfterThrowing 方法则会被执行。此外,在 @Around 环绕通知中只有头部部分会被执行。下面是别的注意事项:
- 一个切面类可以定义多个切点。
- @Around 环绕通知需要调用 ProceedingJoinPoint.proceed() 方法以便执行原始方法,而其他通知不需要考虑目标方法是否执行。
- @Around 环绕通知方法的返回值必须指定为 Objec 以便接收原始方法的返回值;否则在原始方法执行完毕后无法获取返回值。
1.5 @PointCut
上述代码存在一个问题,即大量重复使用切点表达式 execution(* com.example.demo.controller..(…))。为了解决这个问题,Spring 提供了 @Pointcut 注解,可以将公共的切点表达式提取出来。这样,在需要使用时只需引用该切入点表达式即可。因此,可以对代码进行如下修改:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo {// 定义切点(公共的切点表达式)@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 After 方法");}// 返回后通知@AfterReturning("pt()")public void doAfterReturning() {log.info("执行 AfterReturning 方法");}// 抛出异常后通知@AfterThrowing("pt()")public void doAfterThrowing() {log.info("执行 doAfterThrowing 方法");}// 添加环绕通知@Around("pt()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around 方法开始执行");Object result = joinPoint.proceed();log.info("Around 方法结束执行");return result;}
}
当切点定义使用 private 修饰时,该切点只能在当前切面类中使用。如果其他切面类也需要使用这个切点定义,则需要将其访问修饰符改为 public。引用时的格式为:全限定类名.方法名()。
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo2 {// 前置通知@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore() {log.info("执行 AspectDemo2 -> Before 方法");}
}
1.6 切面优先级 @Order
在一个项目中如果定义了多个切面类,并且它们的多个切入点都匹配到同一个目标方法,当该方法执行的时候,这些切面类中的通知方法将依次执行,那么此时这几个通知方法的执行顺序是怎么样的呢?我们看看下面的这段代码:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo2 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo2 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo2 -> After 方法");}
}@Slf4j
@Aspect
@Component
public class AspectDemo3 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo3 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo3 -> After 方法");}
}@Slf4j
@Aspect
@Component
public class AspectDemo4 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo4 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo4 -> After 方法");}
}
访问 http://127.0.0.1:8080/test/t1:
通过上述程序的运行结果可以看出:在存在多个切面类的情况下,切面通知的执行顺序默认是按照类名的字母顺序排列的:对于 @Before 通知,字母排名靠前的切面会优先执行;对于 @After 通知,字母排名靠前的切面则会后执行。然而,这种方式不便于管理,特别是当类名具有实际意义时。为了解决这个问题,Spring 提供了 @Order 注解,用于显式控制切面通知的执行顺序。使用该注解可以根据需要定义切面类的优先级。
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
@Order(2)
public class AspectDemo2 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo2 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo2 -> After 方法");}
}@Slf4j
@Aspect
@Component
@Order(1)
public class AspectDemo3 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo3 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo3 -> After 方法");}
}@Slf4j
@Aspect
@Component
@Order(3)
public class AspectDemo4 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt() {}// 前置通知@Before("pt()")public void doBefore() {log.info("执行 AspectDemo4 -> Before 方法");}// 后置通知@After("pt()")public void doAfter() {log.info("执行 AspectDemo4 -> After 方法");}
}
访问 http://127.0.0.1:8080/test/t1:
通过上述程序的运行结果可以得出结论:使用 @Order 注解标识的切面类,其执行顺序如下:对于 @Before 通知,数字越小的切面优先执行;而对于 @After 通知,数字越大的切面则优先执行。换句话说,@Order 注解控制了切面的优先级,优先执行优先级较高的切面,随后执行优先级较低的切面,最终执行目标方法。
1.7 切点表达式
在上述代码中我们一直使用切点表达式来描述切点。接下来,我们将介绍切点表达式的语法。常见的切点表达式主要有两种形式:
- execution(??):根据方法的签名进行匹配。
- @annotation(??):根据方法上的注解进行匹配。
1.7.1 execution 表达式
execution() 是最常用的切点表达式,用于匹配方法,其语法为如下图所示,其中访问修饰符和异常可以省略。切点表达式支持通配符,主要包括两种:
好的,感谢你的提醒。以下是重新整理后的内容,去掉了反引号和加粗的格式:
-
使用 * :匹配任意字符,只匹配一个元素(返回类型、包、类名、方法或方法参数)。
- 包名使用 * 表示任意包。仅匹配一层包。
- 类名使用 * 表示任意类。
- 返回值使用 * 表示任意返回值类型。
- 方法名使用 * 表示任意方法。
- 参数使用 * 表示一个任意类型的参数。
-
使用 .. :匹配多个连续的任意符号,可以通配任意层级的包,或任意类型、任意数量的参数。
- 使用 .. 配置包名,标识此包及其所有子包。
- 可以使用 .. 配置参数,表示任意个、任意类型的参数。
// 匹配 TestController 下的 public 修饰,返回类型为 String,方法名为 t1,无参方法
execution(public String com.example.demo.controller.TestController.t1())// 省略访问修饰符
execution(String com.example.demo.controller.TestController.t1())// 匹配所有返回类型
execution(* com.example.demo.controller.TestController.t1())// 匹配 TestController 下的所有无参方法
execution(* com.example.demo.controller.TestController.*())// 匹配 TestController 下的所有方法
execution(* com.example.demo.controller.TestController.*(..))// 匹配 controller 包下所有的类的所有方法
execution(* com.example.demo.controller.*.*(..))// 匹配所有包下名为 TestController 的所有方法
execution(* com..TestController.*(..))// 匹配 com.example.demo 包下,子孙包下的所有类的所有方法
execution(* com.example.demo..*(..))
1.7.2 @annotation
execution 表达式更适用有规则的,如果我们要匹配多个无规则的方法呢,这个时候我们使用 execution 这种切点表达式来描述就不是很方便了,我们可以借助自定义注解的方式以及另⼀种切点表达式 @annotation 来描述这⼀类的切点,@annotation 描述切点的方步骤如下:
- 编写自定义注解。
- 使用 @annotation 表达式来定义切点。
- 在连接点的方法上添加自定义注解。
1.7.2.1 自定义注解 @MyAspect
创建一个注解类的流程与创建普通类类似,只需选择 Annotation 类型即可。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 定义自定义注解 MyAspect
@Target(ElementType.METHOD) // 注解适用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
public @interface MyAspect {// 注解属性可以根据需要进行定义String value() default ""; // 默认属性
}
这段代码比较简单,我们这里简单讲解了解即可,@Target 标识了 Annotation 所修饰的对象范围,即该注解可以用在什么地方,@Retention 指 Annotation 被保留的时间长短,标明注解的生命周期。
ElementType | 描述 |
---|---|
ElementType.TYPE | 用于描述类、接口(包括注解类型)或 enum 声明 |
ElementType.METHOD` | 描述方法 |
ElementType.PARAMETER | 描述参数 |
ElementType.TYPE_USE | 可以标注任意类型 |
RetentionPolicy | 描述 |
---|---|
RetentionPolicy.SOURCE | 表示注解仅存在于源代码中,编译成字节码后会被丢弃。这意味着在运行时无法获取到该注解的信息,只能在编译时使用。 |
RetentionPolicy.CLASS | 编译时注解,表示注解存在于源代码和字节码中,但在运行时会被丢弃。这意味着在编译时和字节码中可以通过反射获取到该注解的信息,但在实际运行时无法获取。 |
RetentionPolicy.RUNTIME | 运行时注解,表示注解存在于源代码、字节码和运行时中。这意味着在编译时、字节码中和实际运行时都可以通过反射获取到该注解的信息。 |
1.7.2.2 编写切面类并添加自定义注解
使用 @annotation 切点表达式定义切点,只对 @MyAspect 生效,切面类代码如下:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Slf4j
@Component
@Aspect
public class MyAspectDemo {// 前置通知@Before("@annotation(com.example.demo.aspect.MyAspect)")public void before() {log.info("MyAspect -> before ...");}// 后置通知@After("@annotation(com.example.demo.aspect.MyAspect)")public void after() {log.info("MyAspect -> after ...");}
}
添加自定义注解 @MyAspect:
@MyAspect
@RequestMapping("/t1")
public String t1() {return "t1";
}
访问 http://127.0.0.1:8080/test/t1:
1.8 Spring AOP 的实现方式
方法 | 描述 |
---|---|
基于注解 @Aspect | 利用 Spring AOP 提供的 @Aspect 注解功能来实现切面编程。 |
基于自定义注解 | 使用自定义注解结合 @annotation 来定义切点,灵活地控制切面的执行。 |
基于 Spring API | 通过 XML 配置的方式实现 AOP,这种方法在 Spring Boot 广泛使用之后几乎被淘汰,但可以作为补充了解。 |
基于代理实现 | 一种较早的实现方式,通过代理方式实现 AOP,写法相对繁琐,不推荐使用。 |
1.9 Spring AOP 原理
偷个懒,下次有空了再补。。。。。。
相关文章:
Spring - 13 ( 11000 字 Spring 入门级教程 )
一: Spring AOP 备注:之前学习 Spring 学到 AOP 就去梳理之前学习的知识点了,后面因为各种原因导致 Spring AOP 的博客一直搁置。。。。。。下面开始正式的讲解。 学习完 Spring 的统一功能后,我们就进入了 Spring AOP 的学习。…...
SQL 解析 with as dual sysdate level
目录 sql的运行顺序 with as EXTRACT 编辑 dual sysdate level 编辑 编辑 Oracle中的日期存储 核心部分 拆解字符串并计算最小值 关联子查询 NVL 函数 REGEXP_SUBSTR() sql的运行顺序 <select id"getTrendList" parameterType"java.util.H…...
苍穹外卖day03
店铺状态接口 引入Redis,因为像存储店铺状态这种只有一个字段(没必要存储在数据库),且登录后台就要被访问的数据(加快查询速度,减少数据库压力) 使用步骤:导入相关maven依赖、配置…...
精品整理 | 云安全知识证书 (CCSK) v5 备考学习资源汇总
云安全知识证书 (CCSK) v5 备考学习资源,包含课件、视频、习题及CSA学习指南,共12章。 1.云计算的概念和架构 2.云治理 3.风险、审计与合规 4.组织管理 5.身份和访问管理 6.云安全监控 7.云基础设施和网络安全 8.云工作负载安全 9.云数据安全 10.云应用…...
编程思想——FP、OOP、FRP、AOP、IOC、DI、MVC、DTO、DAO
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…...
使用SSH开通Linux服务器账号
文章目录 1. 通过SSH连接到服务器2. 创建账号3. 将用户设置为管理员(可选)4. 设置SSH登录权限(可选)(1)切换到该用户目录(2)创建.ssh目录并设置适当的权限 1. 通过SSH连接到服务器 …...
【C++】内存分配与释放、内存碎片、内存泄漏、栈溢出
C内存分配方式 内存分配方式区别 特性 静态分配 栈分配 堆分配 分配时机 编译期 函数调用时 运行期(new) 释放方式 自动释放 函数结束自动释放 手动delete释放 内存区域 静态存储区 栈 堆(自由存储区) 大小灵活性…...
论文:Generalized Category Discovery with Large Language Models in the Loop
论文下载地址:Generalized Category Discovery with Large Language Models in the Loop - ACL Anthology 1、研究背景 尽管现代机器学习系统在许多任务上取得了优异的性能,绝大多数都遵循封闭世界的设置,假设训练和测试数据来自同一组预定义…...
k8s亲和力和非亲和力
在 Kubernetes 中,亲和力(Affinity)和非亲和力(Anti-Affinity)是用于控制 Pod 调度策略的机制,它们可以帮助优化资源利用率、提高应用性能和可用性。以下是亲和力和非亲和力的详细解释: 亲和力…...
Redis几个基本的全局指令
目录 1.set和get 2.keys 3.exists 4.del 5.expire 6.ttl 7.type 我们都知道Redis存的内容都是键值对,key是String,value有很多类型,像string(字符串),hash(哈希),…...
Flutter中如何判断一个计算任务是否耗时?
在 Flutter 里,判断一个计算任务是否耗时可从以下几个角度着手: 1. 任务复杂度分析 数学运算复杂度:依据算法的时间复杂度来初步判断。例如,简单的加法、乘法运算时间复杂度为 O ( 1 ) O(1) O(1),这类任务通常不耗时…...
LeetCode面试热题150中6-11题学习笔记(用Java语言描述)
Day 02 6、轮转数组 需求:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 方法一 核心思想 使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度,遍历原数组,将原数组下标…...
驱动开发硬核特训 · Day 10 (理论上篇):设备模型 ≈ 运行时的适配器机制
🔍 B站相应的视屏教程: 📌 内核:博文视频 - 总线驱动模型实战全解析 敬请关注,记得标为原始粉丝。 在 Linux 驱动开发中,设备模型(Device Model)是理解驱动架构的核心。而从软件工程…...
4.13日总结
javafx中实现发送qq邮箱验证码: 手动导入jar包方法: 第一步:开启QQ邮箱的 POP3/IMAP 或者 SMTP/IMAP 服务 打开qq邮箱(电脑端),找到设置里的账号与安全的安全设置,往下滑就可以找到 POP3/IMAP 或者 SMTP…...
python 微博爬虫 01
起因, 目的: ✅下载单个视频,完成。✅ 获取某用户的视频列表,完成。剩下的就是, 根据视频列表,逐个下载视频,我没做,没意思。获取视频的评论,以后再说。 关键点记录: 1. 对一个视…...
CST1017.基于Spring Boot+Vue共享单车管理系统
计算机/JAVA毕业设计 【CST1017.基于Spring BootVue共享单车管理系统】 【项目介绍】 共享单车管理系统,基于 Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有四类用户,分别是:监管用户、运营用户、调度用户、普…...
小刚说C语言刷题——第23讲 字符数组
前面,我们学习了一维数组和二维数组的概念。今天我们学习一种特殊的数组,字符数组。 1.字符数组的概念 字符数组就是指元素类型为字符的数组。字符数组是用来存放字符序列或者字符串的。 2.字符数组的定义及语法 char ch[5]; 3.字符数组的初始化及赋…...
c++11--std::forwaord--完美转发
std::forword的作用 完美转发的核心目的是保持参数的原始类型(包括const/volatile限定符和左值/右值性质)不变地传递给其他函数。 为什么需要完美转发 在没有完美转发之前,我们面临以下问题: 模板参数传递中的值类别丢失 当参数…...
机器学习的一百个概念(12)学习率
前言 本文隶属于专栏《机器学习的一百个概念》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索: 知识库创建人机器学习@Shockang机器学习数学基础@Shocka…...
java异常 与 泛型<T>
文章目录 异常认识异常什么是异常?Java的异常体系异常的基本处理异常的作用? 自定义异常编译时异常自定义运行时异常 异常的处理方案 泛型认识泛型泛型类泛型接口泛型方法、通配符、上下限泛型支持的类型包装类包装类具备的其他功能总结 异常 认识异常 …...
齐次坐标系统:什么是齐次坐标?为什么要引入齐次坐标?
齐次坐标系统:计算机图形学的基础 在计算机图形学、计算机视觉、相机标定、三维建模等领域,齐次坐标是一个非常重要的数学工具。本文将介绍:齐次坐标的基本概念、数学原理、我们为什么要引入齐次坐标、及其在实际应用中的价值。 文章目录 齐…...
基于XGBoost的异烟酸生产收率预测:冠军解决方案解析
1. 引言 在化工生产领域,准确预测产品收率对优化工艺流程、降低生产成本具有重要意义。本文以异烟酸生产为研究对象,通过机器学习方法构建预测模型,在包含10个生产步骤、42个工艺参数的数据集上实现高精度收率预测。该方案在工业竞赛中斩获冠军,本文将深度解析其技术实现细…...
vue3动态路由
要想实现vitevue-router实现动态路由我们需要用到 1. addRoute() 官方文档中addRoute的使用 当我们添加一个主路由的时候 router.addRoute({ path: /permission, name: permission, component: () > import(xxxxx)}) 添加子路由也就是嵌套路由 router.addRoute(主路由的…...
Tkinter进度条与状态栏
在图形用户界面(GUI)应用中,进度条和状态栏是非常常见的控件,它们可以有效地向用户显示操作进度、状态信息或者任务完成情况。Tkinter提供了内置的控件和方法来实现进度条和状态栏的功能。在这一章中,我们将学习如何在Tkinter应用中使用进度条和状态栏来提升用户体验。 1…...
NModbus 库在 C# 中的使用
以下是关于 NModbus 库在 C# 中的使用方法 的详细指南,涵盖从安装到实际通信的完整流程: 1. 安装 NModbus 库 通过 NuGet 包管理器安装: Install-Package NModbus 或使用 .NET CLI: dotnet add package NModbus 2. 基础使用示例 2.1 创建 Modbus TCP 主站(Master) …...
大模型到底是怎么产生的?一文了解大模型诞生全过程
前言 大模型到底是怎么产生的呢? 本文将从最基础的概念开始,逐步深入,用通俗易懂的语言为大家揭开大模型的神秘面纱。 大家好,我是大 F,深耕AI算法十余年,互联网大厂核心技术岗。 知行合一,不写水文,喜欢可关注,分享AI算法干货、技术心得。 【专栏介绍】: 欢迎关注《…...
算法差分详解 + 总结
文章目录 差分一维差分题解代码 二维差分 差分 区间修改时使用差分 1. 先预处理一个差分数组,cre[i] a[i] - a[i-1],对差分数组求前缀和可以还原为原数组 2. 如果要让区间内的数d,比如[l,r]内d,那么r1区间-d可以达到这样的效果&…...
全星APQP软件:为用户提供高效、合规、便捷的研发管理体验
全星APQP软件:为用户提供高效、合规、便捷的研发管理体验 为什么选择全星APQP软件系统? 在汽车及高端制造行业,研发项目管理涉及APQP(先期产品质量策划)、FMEA(失效模式与影响分析)、CP&#x…...
数据结构——哈希详解
数据结构——哈希详解 目录 一、哈希的定义 二、六种哈希函数的构造方法 2.1 除留取余法 2.2 平方取中法 2.3 随机数法 2.4 折叠法 2.5 数字分析法 2.6 直接定值法 三、四种解决哈希冲突的方法 3.1 开放地址法 3.1.1 线性探测法 3.1.2 二次探测法 3.2 链地址法 3…...
智慧乡村数字化农业全产业链服务平台建设方案PPT(99页)
1. 农业全产业链概念 农业全产业链是依托数字化、电子商务、云计算等技术,整合规划咨询、应用软件设计与开发等服务,推动农业产业升级和价值重塑,构建IT产业融合新生态。 2. 产业链技术支撑 利用云计算、大数据、区块链等技术,为…...
Mysql -- 基础
SQL SQL通用语法: SQL分类: DDL: 数据库操作 查询: SHOW DATABASES; 创建: CREATE DATABASE[IF NOT EXISTS] 数据库名 [DEFAULT CHARSET字符集] [COLLATE 排序规则]; 删除: DROP DATABA…...
《AI大模型应知应会100篇》第14篇:大模型商业化现状:主流应用场景及盈利模式
第14篇:大模型商业化现状:主流应用场景及盈利模式 摘要 近年来,大模型(如Qwen、DeepSeek、GPT、BERT等)以其强大的语言理解和生成能力引发了技术界的广泛关注。然而,如何将这些前沿技术转化为商业价值&am…...
深入理解linux操作系统---第3讲 基本操作与基本管理
3.1 shell基本功能与基本概念 3.1.1 shell基本功能 Shell是Linux系统的核心交互工具,主要功能包括: 程序启动与进程管理:通过命令行将程序名传递给内核执行,支持进程的后台运行与监控(如ps、kill命令)文…...
Go:函数
函数 函数声明 func name(parameter-list) (result-list) { body }函数声明包含函数名、形参列表、可选的返回列表以及函数体 。形参列表指定由调用者传递的变量参数名和类型;返回列表指定函数返回值类型 ,无返回值或返回未命名值时,返回列…...
swagger 注释说明
一、接口注释核心字段 在 Go 的路由处理函数(Handler)上方添加注释,支持以下常用注解: 注解名称用途说明示例格式Summary接口简要描述Summary 创建用户Description接口详细说明Description 通过用户名和邮箱创建新用户Tags接口分…...
【C++】C与C++、C++内存空间、堆与栈
C嘎嘎嘎嘎嘎~ C与C的区别与联系 C内存空间 int global_var; // 未初始化全局变量,BSS段 const char* str "Hello"; // 字符串常量text段 in数据段void func() {static int static_var; // 未初始化的静态变量,数据段int local_var; …...
从零训练LLM-1.训练BPE
文章目录 BPE 简介BPE (Byte-Pair Encoding) 算法训练流程BPE 编码流程BPE 评估代码 参考 本文基于 HF -tokenizer 训练,更便捷 BPE 简介 分词器将单词从自然语言通过“词典”映射到0, 1, 36这样的数字,可以理解为数字就代表了单词在“词典”中的页码。…...
shield.io网站|markdown中适用的“徽标”
动态的我还没看是怎么弄,但是应该和静态的差不多,因此本文仅讨论静态徽标 静态徽标效果 创建方法 网址:Shields.io | Shields.io 进入之后点击“Badges”标签进入网页创建徽标的页面,根据文档中对每个属性的介绍在右侧将自己预…...
Vue 3 自定义指令
Vue 3 是一个非常强大的前端框架,它不仅提供了简单易用的 API,还支持多种高级功能,以便开发者根据需要扩展和优化应用。在 Vue 中,自定义指令是一种非常灵活的功能,它允许我们为 DOM 元素添加特定的行为,扩…...
25某团校招后端开发一面
一、进程通信和线程通信方式 进程通信方式 管道(Pipe) 半双工通信,数据单向流动,仅用于有亲缘关系的进程(如父子进程)。通过内核缓冲区实现数据传输,如父进程写、子进程读。命名管道ÿ…...
音视频学习(三十四):H264中的宏块
什么是宏块? 在 H.264 中,宏块是编码图像时最小的处理单位。它的核心作用包括: 帧内预测(Intra Prediction)帧间预测(Inter Prediction)变换、量化、熵编码等 标准定义: 一个宏块…...
Pandas 中透视表(`pivot_table`)和交叉表(`crosstab`)的区别
Pandas 中透视表(pivot_table)和交叉表(crosstab)的区别 核心区别 透视表 (pivot_table) 用于对数据进行 聚合计算(如求和、均值、计数等)。支持多维度分组(行、列、甚至多层索引)。…...
Restful风格接口开发
目录 Restful Apifox 介绍 端口号8080怎么来的? 为什么要使用Apifox? Restful 如果请求方式是Post,那我就知道了要执行新增操作,要新增一个用户 如果请求方式是Put,那就代表我要修改用户 具体要对这些资源进行什么样的操…...
20250414| AI:RAG多路召回和融合重排序技术
好的!以下是对RAG(检索增强生成)中多路召回和融合重排序技术的详细解释,结合解释学习的视角,帮助你更好地理解和学习。这些技术是RAG系统的核心组成部分,决定了检索阶段的效果和最终生成答案的质量。我会尽…...
基于时间序列分解与XGBoost的交通通行时间预测方法解析
一、问题背景与数据概览 在城市交通管理系统中,准确预测道路通行时间对于智能交通调度和路径规划具有重要意义。本文基于真实道路传感器数据,构建了一个结合时间序列分解与机器学习模型的预测框架。数据源包含三个核心部分: 道路通行数据(new_gy_contest_traveltime_train…...
论文精度:HeightFormer:基于Transformer的体素高度预测在路边3D目标检测中的应用
论文地址:https://arxiv.org/pdf/2503.10777 1. 背景与问题定义 1.1 路边视觉3D检测的重要性 在自动驾驶领域,车辆端的视觉感知系统面临视角局限性(如遮挡、短距离感知)和安全挑战。相比之下,路边摄像头通过高位安装,可覆盖更广的感知范围(如交叉路口、高速公路)…...
华为手机清理大数据的方法
清理手机最大的问题是,手动和自动清理了多次,花费了很长时间,但是只腾挪出来了一点点空间,还是有很大空间无法使用,这篇文章就告诉你怎样做,以花瓣剪辑为例,如下: 删除数据ÿ…...
tcp特点+TCP的状态转换图+time_wait详解
tcp特点TCP的状态转换图time wait详解 目录 一、tcp特点解释 1.1 面向连接 1.1.1 连接建立——三次握手 1.1.2 连接释放——四次挥手 1.2 可靠的 1.2.1 应答确认 1.2.2 超时重传 1.2.3 乱序重排 1.2.4 去重 1.2.5 滑动窗口进行流量控制 1.3 流失服务(字节…...
flutter 桌面应用之窗口自定义
在开发桌面软件的时候我们经常需要配置软件的窗口的大小以及位置 我们有两个框架选择:window_manager和bitsdojo_window 对比bitsdojo_window 特性bitsdojo_windowwindow_manager自定义标题栏✅ 支持❌ 不支持控制窗口行为(大小/位置)✅(基本…...
【C++】NAN相关研究
先说结论:NAN对比一切都是false INF 对INF 是true 正无穷与正无穷比较相等,正无穷与负无穷比较不相等 window linux环境下基本相同, debug release基本相同 NAN -NAN INF -INF 不做论述 // TestNan.cpp : 此文件包含 "main" 函数。…...