【Spring】深入探索 Spring AOP:概念、使用与实现原理解析
文章目录
- 前言
- 一、初识 Spring AOP
- 1.1 什么是 AOP
- 1.2 什么是 Spring AOP
- 二、AOP 的核心概念
- 2.1 切面(Aspect)
- 2.2 切点(Pointcut)
- 2.3 通知(Advice)
- 2.4 连接点(Join Point)
- 三、Spring AOP 的使用
- 3.1 添加 Spring AOP框架支持
- 3.2 定义切面和切点
- 3.3 定义相关的通知方法
- 四、Spring AOP 的实现原理
- 4.1 动态代理
- 4.2 JDK 动态代理
- 4.3 CGLIB 动态代理
- 4.4 JDK Proxy 和 CGLIB 动态代理的区别
前言
在现在的软件开发中,面向对象编程(Object-Oriented Programming,即 OOP)已经成为主流的编程范式,它以对象作为程序的基本构件单元,通过封装、继承和多态等特性来组织代码,实现可维护性和可扩展性的应用程序。但是,随着软件规模的不断扩大和复杂度的增加,OOP 在某些情况下可能就面临一些挑战了。
一个常见的问题是,业务逻辑和横切关注点(cross-cutting concerns)
之间的耦合问题。所谓的 横切关注点就是那些在应用程序中分布广泛,与核心业务逻辑代码相互交织在一起的功能
,例如登录判断、日志记录、事务管理、权限控制等 。将这些关注点与核心业务逻辑紧密耦合在一起会导致代码的重复和难以维护。因此,在这种情况下,一种面向切面编程(Aspect-Oriented Programming,简称 AOP)的编程范式应运而生。
一、初识 Spring AOP
在传统的面向对象编程中,我们通过封装、继承和多态等方式来组织代码,将相关的功能逻辑封装到对象的方法中。然而,在实际开发中,除了核心的业务逻辑,往往还有一些与之关系不大但却必要的功能,如日志记录、性能监测、安全控制等。这些功能通常会散布在代码中的多个地方,导致代码的重复性和难以维护性。
1.1 什么是 AOP
面向切面编程(Aspect-Oriented Programming,简称 AOP)正是为了解决这种代码重复性和难以维护的问题而产生的一种编程范式。AOP 的核心思想是将横切关注点(cross-cutting concerns)从主要的业务逻辑中分离出来,以模块化的方式进行管理
。横切关注点是那些在应用程序中分布广泛、与核心业务逻辑交织在一起的功能,例如日志、事务、权限等。
AOP 将系统划分为两个主要部分:核心关注点和横切关注点
。核心关注点即应用程序的主要业务逻辑,而横切关注点则是与核心关注点无关但却必要的功能。
AOP 通过将横切关注点抽象成称为切面(Aspect)的模块,然后将其独立于核心业务逻辑进行管理和维护。这样一来,我们可以在不修改主要业务逻辑的情况下,灵活地添加、修改或删除各种功能,从而提高了代码的可维护性和可扩展性。
1.2 什么是 Spring AOP
就如同 IoC 与 DI 之间的关系一样,AOP是一种思想,而 Spring AOP 则是 AOP 思想的一种实现
。同时,Spring AOP也是 Spring 框架中的一个重要模块,它允许开发者能够轻松地实现面向切面编程。
简单来说,Spring AOP 建立在传统的 AOP 概念之上,提供了一种更简单和便捷方式来管理横切关注点。它通过代理技术在核心关注点的前、后或环绕执行切面的逻辑,从而实现了横切关注点的模块化。与传统的面向对象编程相比,使用 Spring AOP 可以更好地分离关注点,使得代码更加清晰、可维护性更高。
Spring AOP 支持两种类型的代理:基于接口的 JDK 动态代理
和基于类的 CGLIB 动态代理
。它还提供了一系列的注解和配置选项,让开发者能够灵活地定义切面和通知(Advice),并将其应用到不同的目标对象上。
二、AOP 的核心概念
在面向切面编程(AOP)中,有几个核心概念是关键的,它们帮助我们理解如何实现横切关注点的管理和应用。这些概念包括切面、切点、通知和连接点,它们一起构成了 AOP 的基础。
2.1 切面(Aspect)
切面(Aspect)由切点(Pointcut)和通知(Advice)组成,它是对横切关注点的抽象化
。它定义了在哪些切点上应该执行什么样的横切逻辑。切面是一个模块化的单元,它将通用的横切关注点从主要业务中分离出来,以便更好的管理和维护。
更加通俗地说,切面就相当于 Java 中的一个类,它代表了某一方面的具体内容,类似于一个代码模块。
- 举例来说,我们可以把用户登录判断看作一个“切面”,其中包含了在用户登录过程中需要执行的逻辑。同样地,日志的统计记录也是一个“切面”,其中包括了在代码执行时需要记录日志的相关操作。
- 切面就像是一个装有特定功能的工具箱,每个工具箱里都放着一组相关的操作。在代码中,切面定义了在哪些地方(切点)以及如何执行这些操作。当我们需要在不同的地方应用相似的功能时,我们可以创建一个切面,将相关的操作封装在里面。
2.2 切点(Pointcut)
切点(Pointcut)可以被理解为匹配连接点(Join Point)的谓词,或者说它是一组规则的集合,用于选择在哪些连接点上应用横切逻辑
。切点使用 AspectJ pointcut expression language
来描述这些规则,该语言允许您灵活地定义匹配条件,以便精确地选择特定的连接点。
AspectJ pointcut expression language
是一种用于定义切点(Pointcut)的表达式语言,它是 AspectJ 框架中的一个关键部分。这个表达式语言允许我们灵活地描述在哪些连接点上应该应用通知(Advice)。
切点的作用就是为我们提供了一种方式来定义规则,以便筛选出满足条件的连接点。当连接点满足切点定义的规则时,我们就可以将通知(Advice)应用到这些连接点上,从而在这些特定的位置插入横切逻辑。这使得我们可以有选择地在代码中插入特定的行为,而不需要将横切关注点与主要业务逻辑耦合在一起。
2.3 通知(Advice)
在 AOP 中,切面不仅仅是一个抽象概念,它是有着明确目标的。切面需要完成特定的任务,而在 AOP 术语中,这些任务被称为通知(Advice)。通知定义了切面的具体行为:它解决了切面是什么、何时使用以及在何时执行这些行为的问题
。
在 Spring 的切面类中,可以使用不同的注解来标记方法作为通知方法,这些通知方法在满足条件时会被调用:
-
前置通知使用
@Before
:通知方法会在目标方法调用之前执行,可以执行一些预处理操作。 -
后置通知使用
@After
:通知方法会在目标方法返回或抛出异常后调用,可以执行一些善后操作。 -
返回之后通知使用
@AfterReturning
:通知方法会在目标方法成功返回后调用,可以处理返回值。 -
抛异常后通知使用
@AfterThrowing
:通知方法会在目标方法抛出异常后调用,可以处理异常情况。 -
环绕通知使用
@Around
:通知包裹了被通知的方法,在目标方法调用之前和之后执行自定义的行为,也可以控制是否调用目标方法以及如何处理返回值。
2.4 连接点(Join Point)
连接点代表了程序执行过程中的实际点,它包括了所有能够触发切点的点。换句话说,连接点是切点在代码中的实际发生的实例。通知(Advice)会在连接点上执行,从而实现横切关注点的添加或修改。连接点是 AOP 中被拦截的点。
AOP 整个组成部分的概念如下图所示,以多个页面都要访问用户登录权限为例:
除了切面、切点、通知和连接点之外,AOP 还涉及一些其他重要的概念和术语。以下是一些相关的概念:
-
引入(Introduction): 引入是一种特殊类型的通知,它允许向现有的类添加新方法或字段。因此实现了可以将新功能引入到现有的类中,而无需修改其代码。
-
目标对象(Target Object): 目标对象是应用程序中的对象,被切面影响和拦截的对象。切面会在目标对象的连接点上应用通知。
-
代理(Proxy): 代理是切面对目标对象的包装。它允许切面将通知插入到目标对象的连接点上,实现横切逻辑。代理可以通过静态代理、JDK 动态代理或 CGLIB 动态代理来实现。
-
织入(Weaving): 织入是将切面与目标对象连接的过程。在编译时、加载时或运行时,切面的通知被插入到目标对象的连接点上,从而实现横切关注点的逻辑。
-
通知顺序(Advice Order): 如果在一个切点上有多个通知,通知的执行顺序可能很重要。通知顺序定义了多个通知在切点上执行的先后顺序。
-
切面优先级(Aspect Priority): 如果在应用中存在多个切面,切面的优先级可以影响通知的执行顺序。切面优先级决定了多个切面之间的先后执行顺序。
-
动态切面(Dynamic Aspects): 动态切面是在运行时根据某些条件确定是否要应用切面的一种机制。允许根据需要动态地选择是否将切面应用于目标对象。
三、Spring AOP 的使用
以上的概念可以说都是晦涩难懂的,下面通过使用 Spring AOP 来模拟实现一下 AOP 的功能,可以更好的帮助我们理解 AOP。此时我们完成的目标是拦截 UserController
中的所有方法,每次调用其中一个方法的时候,都会执行相应的通知事件。
使用 Spring AOP 的步骤如下:
- 添加 Spring AOP框架支持;
- 定义切面和切点;
- 定义相关的通知方法。
3.1 添加 Spring AOP框架支持
在 pom.xml
中添加如下配置:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.7.14</version>
</dependency>
3.2 定义切面和切点
切面代表的是某一方面的具体内容,比如用户登录权限的判断,而此处的切面就是对UserController
类的处理。其中的就是就是定义具体的拦截规则。例如下面的代码:
@Aspect // 表示定义一个切面(类)
@Component
public class UserAspect {// 创建切点(方法)定义拦截规则@Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")public void pointcut() {}
}
在这段代码中,创建了一个切面类 UserAspect
,并使用 @Aspect
注解标记它为一个切面。同时,使用 @Component
注解将这个切面声明为一个 Spring 管理的组件,以便让 Spring 容器能够管理和识别它。
在 UserAspect
类中,使用 @Pointcut
注解定义了一个切点,该切点的拦截规则是匹配 UserController
类中的所有公共方法(public * com.example.demo.controller.UserController.*(..))
。这意味着我们的切点会在 UserController
类的所有公共方法上触发。
需要注意的是,pointcut
方法的方法体可以为空,因为 @Pointcut
注解仅用于定义切点表达式,实际的逻辑代码会在通知方法中实现。
切点表达式说明:
切点表达式是用来定义切点的匹配规则,它决定了在哪些连接点上应用通知。AspectJ 支持三种通配符来构建切点表达式:
*
:匹配任意字符,只匹配一个元素(包、类、或方法、方法参数)。..
:匹配任意字符,可以匹配多个元素。在表示类时,必须和*
联合使用,例如:com.cad..*
表示com.cad
包下的所有子孙包中的所有类。+
:表示按照类型匹配指定类的所有类。必须跟在类名后面,如com.cad.Car+
表示继承该类的所有子类包括本身。切点表达式通常使用
execution()
切点函数,它是最常用的用来匹配方法的切点函数。
切点表达式示例:
以下是一些切点表达式的示例,以便更好地理解如何构建切点规则:
execution(* com.cad.demo.User.*(..))
:匹配User
类中的所有方法。execution(* com.cad.demo.User+.*(..))
:匹配User
类的子类中的所有方法,包括本身。execution(* com.cad.*.*(..))
:匹配com.cad
包下所有类的所有方法。execution(* com.cad..*.*(..))
:匹配com.cad
包下、以及其所有子孙包中的所有类的所有方法。execution(* addUser(String, int))
:匹配addUser
方法,且第一个参数类型是String
,第二个参数类型是int
。
3.3 定义相关的通知方法
通知定义的是被拦截的方法具体要执行的业务,比如用户登录权限验证方法就是具体要执行的业务,而此次只是进行简单的打印输操作。在 Spring AOP 中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:
-
前置通知使用
@Before
:通知方法会在目标方法调用之前执行,可以执行一些预处理操作。 -
后置通知使用
@After
:通知方法会在目标方法返回或抛出异常后调用,可以执行一些善后操作。 -
返回之后通知使用
@AfterReturning
:通知方法会在目标方法成功返回后调用,可以处理返回值。 -
抛异常后通知使用
@AfterThrowing
:通知方法会在目标方法抛出异常后调用,可以处理异常情况。 -
环绕通知使用
@Around
:通知包裹了被通知的方法,在目标方法调用之前和之后执行自定义的行为,也可以控制是否调用目标方法以及如何处理返回值。
此处具体实现的通知代码如下:
// 创建一个切面(类)
@Aspect
@Component
public class UserAscept {// 创建切点(方法)定义拦截规则@Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")public void pointcut() {}// 前置通知@Before("pointcut()")public void doBefore() {System.out.println("执行了前置通知:" + LocalDateTime.now());}// 后置通知@After("pointcut()")public void doAfter() {System.out.println("执行了后置通知:" + LocalDateTime.now());}// 返回后通知@AfterReturning("pointcut()")public void doAfterReturning() {System.out.println("执行了返回后通知:" + LocalDateTime.now());}// 抛异常后通知@AfterThrowing("pointcut()")public void doAfterThrowing() {System.out.println("抛异常后通知:" + LocalDateTime.now());}// 环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) {Object proceed = null;System.out.println("Around 方法开始执行:" + LocalDateTime.now());try {// 执行拦截的方法proceed = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("Around 方法结束执行: " + LocalDateTime.now());return proceed;}
}
在这段代码中,定义了不同类型的通知方法,每个方法都使用了相应的注解标记,如 @Before
、@After
、@AfterReturning
、@AfterThrowing
和 @Around
。这些注解将通知方法与切点(使用 pointcut()
方法定义的切点)关联起来。当调用 UserController
中的方法时,就会被切点拦截,然后执行这些相应的通知方法。
关于上述环绕方法的说明:
其中,环绕通知在 Spring AOP 中是最为特殊和灵活的通知类型。它允许开发人员完全控制拦截的方法调用,包括在目标方法执行前后以及捕获异常时都能够插入自定义的逻辑。这种特性使得环绕通知非常强大,适用于需要全面控制横切逻辑的场景。
在上述代码中展示了如何实现环绕通知。环绕通知使用 @Around
注解标记,通知方法的参数是一个 ProceedingJoinPoint
对象,它允许我们在适当的时候调用 proceed()
方法来执行拦截的方法。
在这个环绕通知方法中:
-
方法的参数
joinPoint
是一个ProceedingJoinPoint
对象,它代表了目标方法的调用点,可以在需要的时候调用proceed()
方法来执行拦截的方法。 -
可以在
proceed()
方法调用前后插入自定义的逻辑,实现特定的横切行为。 -
可以捕获可能抛出的异常,对异常情况进行处理。
-
通过返回值,您可以控制目标方法的返回值或进行适当的返回值处理。
四、Spring AOP 的实现原理
4.1 动态代理
Spring AOP 是建立在动态代理的基础之上的,因此它的支持范围局限于方法级别的拦截。这意味着 Spring AOP 主要用于拦截和管理方法调用,而不能直接拦截类级别的操作。
在 Spring AOP 中,有两种方式来实现动态代理:JDK Proxy 和 CGLIB。默认情况下,Spring AOP 会根据目标类是否实现接口来选择使用哪种代理方式:
-
JDK Proxy: 如果目标类实现了接口,Spring AOP 将使用 JDK Proxy 来生成代理类。JDK Proxy 基于 Java 标准库中的
java.lang.reflect.Proxy
类实现,要求目标类必须实现至少一个接口。 -
CGLIB: 如果目标类没有实现接口,Spring AOP 将使用 CGLIB 来生成代理类。CGLIB 是一个功能强大的第三方库,它可以在运行时动态地创建类的子类,以实现代理。
这意味着,当使用 Spring AOP 时,如果目标类实现了接口,Spring 将会使用 JDK Proxy 来创建代理。如果目标类没有实现接口,Spring 将使用 CGLIB 来创建代理。在某些情况下,您可能需要注意 CGLIB 代理可能会带来的一些细微影响,例如 final 方法无法被拦截等。
4.2 JDK 动态代理
JDK Proxy 是 Java 标准库中的一种动态代理机制。 它基于目标类实现的接口来创建代理对象,从而在方法调用前后插入横切逻辑。JDK Proxy 主要用于拦截实现了接口的类的方法调用。
JDK Proxy 的实现方式:
- 首先,通过实现
InvocationHandler
接口创建一个方法调用处理器。这个处理器定义了在拦截方法调用时要执行的逻辑。 - 然后,使用
Proxy
类来创建代理对象。Proxy
类的newProxyInstance()
方法接受一个ClassLoader
、一个接口数组和一个InvocationHandler
对象作为参数,然后动态生成代理类的字节码,创建代理实例。
JDK Proxy 的限制:
- JDK Proxy 要求目标类必须实现至少一个接口。它无法直接拦截没有实现接口的类的方法调用。
- JDK Proxy 创建的代理对象实现了目标类实现的接口,因此代理对象只能调用接口中定义的方法。
4.3 CGLIB 动态代理
CGLIB 是一个功能强大的第三方库,用于在运行时创建类的子类,从而实现代理。 它的主要特点在于可以在运行时动态地生成类的子类,而不需要目标类实现接口,从而实现拦截和增强目标类的方法调用。
CGLIB 的特点和优势:
- CGLIB 可以代理未实现接口的类。这使得它适用于更广泛的场景,包括那些没有接口的类。
- CGLIB 使用继承机制来实现代理,生成目标类的子类,并在子类中插入横切逻辑。因此,它可以拦截目标类的所有方法,无论是实例方法还是静态方法。
- 由于使用继承来生成代理类,CGLIB 无法代理被声明为
final
的方法。这是一个需要注意的限制。 - CGLIB 的代理速度通常比 JDK Proxy 稍慢,因为它涉及到创建和加载代理类的字节码。
4.4 JDK Proxy 和 CGLIB 动态代理的区别
当使用 Spring AOP 创建代理时,可以选择使用 JDK Proxy 或 CGLIB 来实现动态代理。这两种代理方式有一些区别,以下是它们的总结:
JDK Proxy:
- 基于 Java 标准库中的
java.lang.reflect.Proxy
类实现。 - 要求目标类必须实现至少一个接口,因为它基于接口来创建代理。
- 通过实现代理接口的方法,在方法调用前后插入横切逻辑。
- 由于使用接口作为代理的基础,生成的代理对象只能调用接口中定义的方法。
- 通常情况下,JDK Proxy 的性能相对较高。
CGLIB:
- 是一个第三方库,用于在运行时生成类的子类实现代理。
- 不需要目标类实现接口,可以代理未实现接口的类。
- 通过继承机制,在目标类的子类中插入横切逻辑。
- 能够拦截目标类的所有方法,包括实例方法和静态方法。
- 由于使用继承和生成代理类的字节码,CGLIB 无法代理被声明为
final
的方法。 - 代理速度相对较慢,因为涉及生成和加载代理类的字节码。
如何选择:
- 如果目标类实现了接口,首选使用 JDK Proxy。它提供了更高的性能,适用于基于接口的代理。
- 如果目标类没有实现接口,或者需要拦截未实现接口的类,可以选择使用 CGLIB。它更加灵活,适用于更广泛的代理场景。
相关文章:

【Spring】深入探索 Spring AOP:概念、使用与实现原理解析
文章目录 前言一、初识 Spring AOP1.1 什么是 AOP1.2 什么是 Spring AOP 二、AOP 的核心概念2.1 切面(Aspect)2.2 切点(Pointcut)2.3 通知(Advice)2.4 连接点(Join Point) 三、Sprin…...
LocalDate介绍和使用
1.什么是 LocalDate? 在我们开始之前,让我先简单介绍一下 LocalDate。它是 Java 8 中引入的日期类,用于表示不带时区信息的日期。也就是说,它专注于日期,并忽略了具体的时间。这样,我们就可以专心解决那些…...
三、使用注解形式开发 Spring MVC程序
文章目录 一、环境准备二、配置 web.xml三、配置 SpringMVC-Servlet.xml ,这里不再使用之前那种写法,直接采用注解配置,引入注解支持,配置视图解析器四、编写 Controller(Controller 和 RequestMapping 注解说明&#…...
【Go】常见的四个内存泄漏问题
Goroutine没有顺利结束 1、这里更多的是由于channelforselect导致的,错误的写法导致了发送者或接收者没有发现channel已经关闭,任务已经结束了,却仍然在尝试输入输出https://geektutu.com/post/hpg-exit-goroutine.html Map的remove方法不会…...

【LeetCode-简单】剑指 Offer 29. 顺时针打印矩阵(详解)
题目 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]示例 2: 输入:matrix [[1,2,3,4],[5,6,7,8],[9,10,1…...
TOMCAT基础
tomcat是一个基于Java开发的,开放源代码的web应用服务器。它可以解析html页面中的java代码,执行动态请求,实现动态页面。核心功能是将收到的http请求处理并转发给适当的servlet来处理,然后将响应返回给客户端。 优点 1,…...
自动化集装箱码头建设指南
1 总则 1.0.1 为规范和指导自动化集装箱码头建设,提高自动化集装箱码头建设和装卸自动化应用水平,做到技术先进、经济合理、安全可靠、高效节能、绿色环保,制定本指南。 1.0.2本指南适用于新建、改建和扩建自动化集装箱码头的设计࿰…...
为什么要用redis
高性能 就是把你一些复杂操作耗时查出来的结果(用了600ms),如果确定后面不咋变了,然后但是马上还有很多读请求,那么直接结果放缓存(6ms),后面直接读缓存就好了。 这样,性能就提升了100倍 高并发 说白了就是…...

QT qmake解析
...

【TypeScript】this指向,this内置组件
this类型 TypeScript可推导的this类型函数中this默认类型对象中的函数中的this明确this指向 怎么指定this类型 this相关的内置工具类型转换ThisParameterType<>ThisParameterType<>ThisType TypeScript可推导的this类型 函数中this默认类型 对象中的函数中的this…...
MySQL 深度分页优化
MySQL 深度分页优化 理解总结: 分页使用limit ,前提是要排序好的数据,这时候,就推荐使用带索引的字段排序,因为索引是天然有序的,不需要像是无序的字段一样,全表扫描,如果太大的话…...

如何在CSS中水平居中一个元素?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 margin: 0 auto⭐ 使用 Flexbox 布局⭐ 使用绝对定位和负边距⭐ 使用表格布局⭐ 使用网格布局⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅…...

生信豆芽菜-ESTIMATE预测免疫评分
网址:http://www.sxdyc.com/immuneEstimateScore 一、ESTIMATE预测免疫评分介绍 ESTIMATE(Estimation of STromal and Immune cells in MAlignant Tumor tissues using Expression data)是一种用于预测肿瘤免疫评分的计算方法。它通过分析基因…...

分享一颗能用在TYPE-C接口取电协议芯片LDR6328Q,方便好用
芯片功能:诱导PD充电器输出最大功率,支持最大诱骗20V电压。支持协议:PD/QC/三星AFC/华为SCP等主流快充协议 芯片封装:QFN16,SOP8多封装选择 芯片应用: 桶形连接器替换(BCR),USB-A和…...
【java】Java与SQLite3数据库类型之间对应关系
引 在开发应用程序时,经常需要将数据存储到数据库中。SQLite3 是一种轻量级的嵌入式数据库,广泛应用于移动设备和嵌入式系统。在使用 SQLite3 数据库时,了解 Java 数据类型与 SQLite3 数据库类型之间的对应关系非常重要,以便正确…...

ELK常见部署架构以及出现的问题及解决方案
ELK常见部署架构以及出现的问题及解决方案 ELK 已经成为目前最流行的集中式日志解决方案,它主要是由Beats 、Logstash 、Elasticsearch 、 Kibana 等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案。本文将会介…...
windows使用vscode配置java开发环境
安装JDK 下载windows版本的JDK,解压到一个目录 openJDK下载地址:Archived OpenJDK GA Releases (java.net) 下载 Microsoft Build of OpenJDK | Microsoft Learn 添加环境变量:JAVA_HOME:JDK的解压目录 编辑PATH:…...
centos系统kubeadm安装K8S_v1.27.x容器使用docker(K8S_v1.24版本以后依然使用docker容器管理)
kubeadm安装K8S_v1.27.x容器使用docker 按照需要的文档点击这里下载 一.环境部署 1.1基础环境配置 主机IP 主机名规划 192.168.186.128 k8s-master01 192.168.186.129 k8s-node01 192.168.186.130 k8s-node02 1.2修改机器名称 #永久修改主机名 hostnamectl set-hostname …...
如何使用索引加速 SQL 查询 [Python 版]
推荐:使用 NSDT场景编辑器助你快速搭建可二次编辑器的3D应用场景 假设您正在筛选一本书的页面。而且您希望更快地找到所需的信息。你是怎么做到的?好吧,您可能会查找术语索引,然后跳转到引用特定术语的页面。SQL 中的索引的工作方…...

Oracle 开发篇+Java通过DRCP访问Oracle数据库
标签:DRCP、Database Resident Connection Pooling、数据库驻留连接池释义:DRCP(全称Database Resident Connection Pooling)数据库驻留连接池(Oracle自己的数据库连接池技术) ★ Oracle开启并配置DRCP sq…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...