【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 还涉及一些其他重要的概念和术语。以下是一些相关的概念:

  1. 引入(Introduction): 引入是一种特殊类型的通知,它允许向现有的类添加新方法或字段。因此实现了可以将新功能引入到现有的类中,而无需修改其代码。

  2. 目标对象(Target Object): 目标对象是应用程序中的对象,被切面影响和拦截的对象。切面会在目标对象的连接点上应用通知。

  3. 代理(Proxy): 代理是切面对目标对象的包装。它允许切面将通知插入到目标对象的连接点上,实现横切逻辑。代理可以通过静态代理、JDK 动态代理或 CGLIB 动态代理来实现。

  4. 织入(Weaving): 织入是将切面与目标对象连接的过程。在编译时、加载时或运行时,切面的通知被插入到目标对象的连接点上,从而实现横切关注点的逻辑。

  5. 通知顺序(Advice Order): 如果在一个切点上有多个通知,通知的执行顺序可能很重要。通知顺序定义了多个通知在切点上执行的先后顺序。

  6. 切面优先级(Aspect Priority): 如果在应用中存在多个切面,切面的优先级可以影响通知的执行顺序。切面优先级决定了多个切面之间的先后执行顺序。

  7. 动态切面(Dynamic Aspects): 动态切面是在运行时根据某些条件确定是否要应用切面的一种机制。允许根据需要动态地选择是否将切面应用于目标对象。

三、Spring AOP 的使用

以上的概念可以说都是晦涩难懂的,下面通过使用 Spring AOP 来模拟实现一下 AOP 的功能,可以更好的帮助我们理解 AOP。此时我们完成的目标是拦截 UserController 中的所有方法,每次调用其中一个方法的时候,都会执行相应的通知事件。

使用 Spring AOP 的步骤如下:

  1. 添加 Spring AOP框架支持;
  2. 定义切面和切点;
  3. 定义相关的通知方法。

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() 方法来执行拦截的方法。

在这个环绕通知方法中:

  1. 方法的参数 joinPoint 是一个 ProceedingJoinPoint 对象,它代表了目标方法的调用点,可以在需要的时候调用 proceed() 方法来执行拦截的方法。

  2. 可以在 proceed() 方法调用前后插入自定义的逻辑,实现特定的横切行为。

  3. 可以捕获可能抛出的异常,对异常情况进行处理。

  4. 通过返回值,您可以控制目标方法的返回值或进行适当的返回值处理。

四、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 的实现方式:

  1. 首先,通过实现 InvocationHandler 接口创建一个方法调用处理器。这个处理器定义了在拦截方法调用时要执行的逻辑。
  2. 然后,使用 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。它更加灵活,适用于更广泛的代理场景。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux进程篇】环境变量

【Linux进程篇】环境变量 目录 【Linux进程篇】环境变量基本概念常见环境变量查看环境变量方法测试PATH测试HOME测试SHELL和环境变量相关的命令环境变量的组织方式通过代码如何获取环境变量命令行参数命令行第三个参数通过第三方变量environ获取 本地变量通过系统调用获取或设置…

[HDLBits] Exams/m2014 q4b

Implement the following circuit: module top_module (input clk,input d, input ar, // asynchronous resetoutput q);always(posedge clk or posedge ar) beginif(ar)q<1b0;elseq<d;end endmodule

组合模式(C++)

定义 将对象组合成树形结构以表示部分-整体’的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。 应用场景 在软件在某些情况下&#xff0c;客户代码过多地依赖于对象容器复杂的内部实现结构&#xff0c;对象容器内部实现结构(而非抽象接口)的变化…

虹科干货 | 化身向量数据库的Redis Enterprise——快速、准确、高效的非结构化数据解决方案!

用户期望在他们遇到的每一个应用程序和网站都有搜索功能。然而&#xff0c;超过80%的商业数据是非结构化的&#xff0c;以文本、图像、音频、视频或其他格式存储。Redis Enterprise如何实现矢量相似性搜索呢&#xff1f;答案是&#xff0c;将AI驱动的搜索功能集成到Redis Enter…

项目难点:解决IOS调用起软键盘之后页面样式布局错乱问题

需求背景 &#xff1a; 开发了一个问卷系统重构项目&#xff0c;刚开始开发的为 PC 端&#xff0c;其中最头疼的一点无非就是 IE 浏览器的兼容适配性问题&#xff1b; 再之后项目经理要求开发移动端&#xff0c;简单的说就是写 H5 页面&#xff0c;到时候会内嵌在 App 应用或办…

[数据分析与可视化] Python绘制数据地图5-MovingPandas绘图实例

MovingPandas是一个基于Python和GeoPandas的开源地理时空数据处理库&#xff0c;用于处理移动物体的轨迹数据。关于MovingPandas的使用见文章&#xff1a;MovingPandas入门指北&#xff0c;本文主要介绍三个MovingPandas的绘图实例。 MovingPandas官方仓库地址为&#xff1a;mo…

Vue使用jspdf和html2canvas组件库结合导出PDF文件

效果图&#xff1a; 1、安装依赖&#xff1a; npm install html2canvas --save npm install jspdf --save 或 yarn add html2canvas --save yarn add jspdf --save 2、封装全局调用方法&#xff1a;this.$exportPDF(#id,文件名) 新建js文件&#xff1a;/utils/html2Pdf.js&am…

java spring cloud 企业工程管理系统源码+二次开发+定制化服务 em

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显…

释放马氏距离的力量:用 Python 探索多元数据分析

一、说明 马哈拉诺比斯距离&#xff08;Mahalanobis Distance&#xff09;是一种测量两个概率分布之间距离的方法。它是基于样本协方差矩阵的函数&#xff0c;用于评估两个向量之间的相似程度。Mahalanobis Distance考虑了数据集中各个特征之间的协方差&#xff0c;因此比欧氏距…

判断自己网络所在的NAT类型

文章目录 各NAT类型介绍软件准备流程 各NAT类型介绍 NAT0: OpenInternet&#xff0c;没有经过NAT地址转换&#xff0c;公网IP NAT1: Full Cone NAT&#xff0c;动态家宽可以达到最优的状态&#xff0c;外网设备可以主动发信息给NAT1网络内的设备。 NAT2: Address-Restricted C…

uniapp 自定义手机顶部状态栏不生效问题

想要的效果想淘宝一样&#xff0c;底色覆盖到手机顶部&#xff0c;找了两天都没找到原因&#xff0c;过程很艰苦&#xff0c;直接上结果吧 项目是后来接手的&#xff0c;最终原因出在这&#xff0c; "immersed" : false>设置为 true 就可以了&#xff0c;沉浸式样…

Spring(三):Spring中Bean的生命周期和作用域

前言 在 Spring 中&#xff0c;那些组成应用程序的主体及由 Spring IOC 容器所管理的对象&#xff0c;被称之为 bean。简单地讲&#xff0c;bean 就是由 IOC 容器初始化、装配及管理的对象&#xff0c;除此之外&#xff0c;bean 就与应用程序中的其他对象没有什么区别了。而 b…

了解IL汇编跳转语句

il代码&#xff0c; .assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 5.entrypointldstr "Enter First Number"call void [mscorlib]System.Console::WriteLine (string)call string …

【图像分类】理论篇(2)经典卷积神经网络 Lenet~Densenet

1、卷积运算 在二维卷积运算中&#xff0c;卷积窗口从输入张量的左上角开始&#xff0c;从左到右、从上到下滑动。 当卷积窗口滑动到新一个位置时&#xff0c;包含在该窗口中的部分张量与卷积核张量进行按元素相乘&#xff0c;得到的张量再求和得到一个单一的标量值&#xff0c…

vue+flask基于知识图谱的抑郁症问答系统

vueflask基于知识图谱的抑郁症问答系统 抑郁症已经成为当今社会刻不容缓需要解决的问题&#xff0c;抑郁症的危害主要有以下几种&#xff1a;1.可导致病人情绪低落&#xff1a;抑郁症的病人长期处于悲观的状态中&#xff0c;感觉不到快乐&#xff0c;总是高兴不起来。2.可导致工…

【软件测试】我的2023面试经验谈

最近行业里有个苦涩的笑话&#xff1a;公司扛过了之前的三年&#xff0c;没扛过摘下最近的一年&#xff0c;真是让人想笑又笑不出来。年前听说政策的变化&#xff0c;大家都满怀希望觉得年后行情一片大好&#xff0c;工作岗位激增&#xff0c;至少能有更多的机会拥抱未来。然而…

warning: remember to run ‘libtool --finish /usr/local/1/php-7.4.29/libs

ubuntu上php7.4.33编译安装完成后警告报错&#xff0c;如下所示 # /usr/local/apache2/apr/build-1/libtool --finish /usr/local/soft/php-7.4.33/libs # vim /etc/ld.so.conf.d/local.conf /usr/local/lib /usr/lib64 # ldconfig 或者安装依赖服务&#xff0c;重新编译 #…

vivo手机录屏在哪里?我来告诉你!(2023最新)

“有人知道vivo手机录屏在哪里吗&#xff1f;刚买了最新款的vivo手机&#xff0c;就是找不到录屏功能在哪&#xff0c;刚好最近需要录屏&#xff0c;很烦躁&#xff0c;有人会吗&#xff0c;求求啦” 随着智能手机的普及&#xff0c;录屏功能逐渐成为用户喜爱的功能之一。vivo…

北航基于openEuler构建工业机器人操作系统,打造“开箱即用”的机器人基础软件平台

北京航空航天大学是国家“双一流”建设高校&#xff0c;以建设扎根中国大地的世界一流大学为发展目标。北京航空航天大学在机器人领域一直处于行业前沿&#xff0c;以其亮眼的成果和优秀的师资力量&#xff0c;成为国内机器人领域的重要参与者和建设者。机器人操作系统是机器人…

“深入解析Maven:安装、创建项目和依赖管理的完全指南“

目录 引言Maven的安装创建Maven项目之前的装备工作Eclipse创建新的Maven项目项目依赖管理 总结 引言 Maven是一个流行的项目管理工具&#xff0c;被广泛用于Java项目的构建、依赖管理和部署。它提供了一种简单而强大的方式来管理项目的各个方面&#xff0c;使开发人员能够更专…