深入理解 Spring 框架中的 AOP 技术

news2025/3/25 18:45:35

一、引言

在 Java 开发领域,Spring 框架凭借其强大的功能和丰富的特性,成为了众多开发者构建企业级应用的首选。其中,面向切面编程(AOP)作为 Spring 框架的核心技术之一,为开发者提供了一种全新的程序结构组织方式,能够在不修改原有业务逻辑代码的基础上,实现对程序功能的统一维护和增强。本文将深入探讨 Spring 框架中的 AOP 技术,从概念引入到实际应用,全面解析其原理和使用方法。

二、AOP 概念的引入

在实际的应用开发中,我们常常会遇到这样的场景:在已有的业务逻辑之上,需要添加一些额外的功能,比如权限校验、日志记录、事务管理等。以常见的登录功能为例,假设我们已经实现了基本的登录逻辑,如果要在登录过程中添加权限校验功能,传统的做法可能是直接修改登录相关的源代码。然而,这种方式存在诸多弊端,比如代码的可维护性降低,一旦后续需求发生变化,再次修改代码可能会引发一系列潜在的问题。

而 AOP 技术则提供了另一种解决方案,即不通过修改源代码的方式来添加新的功能。它能够将这些与业务逻辑无关,但又需要在多个地方重复使用的功能(如权限校验)进行横向抽取,独立于业务逻辑之外进行管理,从而实现对程序功能的增强。这种方式不仅提高了代码的可维护性和可重用性,还大大提升了开发效率。

三、AOP 相关的概念

(一)AOP 的概述

AOP,即 Aspect Oriented Programming,面向切面编程。它是一种编程范式,属于软件工程范畴,主要指导开发者如何组织程序结构。AOP 最早由 AOP 联盟提出,并制定了一套规范。Spring 框架将 AOP 思想引入其中,遵循 AOP 联盟的规范。

从技术实现角度来看,AOP 通过预编译方式或者运行期动态代理来实现程序功能的统一维护。它是 OOP(面向对象编程)的延续,在软件开发领域备受关注,也是 Spring 框架的重要组成部分,同时也是函数式编程的一种衍生范型。

利用 AOP,我们可以将业务逻辑的各个部分进行隔离,降低业务逻辑各部分之间的耦合度。例如,将日志记录、事务管理等功能从核心业务逻辑中分离出来,使得这些功能可以独立开发、测试和维护。这样不仅提高了程序的可重用性,还加快了开发进度。AOP 采取横向抽取机制,有效取代了传统纵向继承体系中重复性代码(如事务管理、安全检查、缓存等方面的代码)。学习 AOP 的最大好处在于,我们能够在不修改源代码的前提下,对程序进行增强。

(二)AOP 的优势

  1. 减少重复的代码:在传统的开发模式中,诸如日志记录、权限校验等功能往往需要在多个业务方法中重复编写代码。而使用 AOP,我们可以将这些通用功能集中实现,通过切面的方式应用到相关的业务方法上,避免了大量重复代码的编写。
  1. 提高开发的效率:由于 AOP 能够将通用功能与业务逻辑分离,开发人员可以更加专注于业务逻辑的实现,而无需在每个业务方法中都关注那些通用功能的代码编写。这使得开发过程更加高效,能够更快地完成项目开发。
  1. 维护方便:当通用功能的需求发生变化时,例如日志记录的格式需要调整或者权限校验的规则发生改变,只需要在切面中进行修改,而无需逐个修改涉及到这些功能的业务方法。这大大降低了维护成本,提高了系统的可维护性。

(三)AOP 的底层原理

  1. JDK 的动态代理技术
    • 为接口创建代理类的字节码文件:JDK 动态代理会根据目标接口动态生成一个代理类的字节码文件。在这个过程中,代理类会实现目标接口,并在代理方法中调用实际目标对象的方法,同时可以在调用前后添加额外的逻辑,即我们所说的通知。
    • 使用 ClassLoader 将字节码文件加载到 JVM:生成的代理类字节码文件需要被加载到 Java 虚拟机(JVM)中才能使用。ClassLoader 负责将字节码文件加载到 JVM 内存中,使得程序可以使用该代理类。
    • 创建代理类实例对象,执行对象的目标方法:当需要使用代理对象时,通过反射机制创建代理类的实例对象。在调用代理对象的方法时,实际会执行代理类中重写的方法,在这个方法中,会先执行通知逻辑,然后调用目标对象的实际方法。
  1. cglib 代理技术

cglib 代理技术则是为类生成代理对象,无论被代理类是否有接口都可以使用。它的底层原理是通过生成被代理类的子类来实现代理功能。在子类中,对被代理类的方法进行拦截和增强,同样可以在方法调用前后添加通知逻辑。

四、Spring 的 AOP 技术 - 配置文件方式

(一)AOP 相关的术语

  1. Joinpoint (连接点):在一个类中,那些可以被增强的方法被称为连接点。简单来说,就是程序执行过程中能够插入额外逻辑的位置,通常是方法调用。
  1. Pointcut (切入点):切入点是对哪些连接点进行拦截的定义。它通过切入点表达式来指定,用于确定具体要对哪些方法进行增强操作。例如,我们可以通过切入点表达式指定只对某个类中的特定方法进行增强。
  1. Advice (通知 / 增强):通知是指拦截到连接点之后所要执行的操作。通知可以分为前置通知、后置通知、异常通知、最终通知和环绕通知等类型。这些通知代表了切面要完成的具体功能。比如,前置通知在目标方法执行前执行,后置通知在目标方法执行成功后执行等。
  1. Aspect (切面):切面是切入点和通知的结合。在实际开发中,我们需要自己编写和配置切面,将切入点表达式与相应的通知关联起来,以实现对特定方法的增强功能。

(二)基本准备工作

AspectJ 是一个面向切面的框架,它对 Java 语言进行了扩展,定义了 AOP 语法。实际上,AspectJ 是对 AOP 编程思想的一种实践。在使用 Spring 的 AOP 技术时,我们常常会借助 AspectJ 的相关功能。

(三)AOP 配置文件方式的入门

  1. 创建 maven 项目,添加坐标依赖:首先,我们需要创建一个 maven 项目,并在项目的 pom.xml 文件中添加相关的依赖坐标。这些依赖包括 Spring 的核心上下文依赖、日志依赖、测试依赖以及 AOP 相关的依赖,如 aopalliance、spring-aspects 和 aspectjweaver 等。具体的依赖配置如下:
 

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>5.0.2.RELEASE</version>

</dependency>

<dependency>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

<version>1.2</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>5.0.2.RELEASE</version>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.12</version>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

<scope>test</scope>

</dependency>

<!--AOP联盟-->

<dependency>

<groupId>aopalliance</groupId>

<artifactId>aopalliance</artifactId>

<version>1.0</version>

</dependency>

<!--Spring Aspects-->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aspects</artifactId>

<version>5.0.2.RELEASE</version>

</dependency>

<!--aspectj-->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.3</version>

</dependency>

</dependencies>

  1. 创建被增强的类:接下来,我们创建一个被增强的类。例如,创建一个名为 User 的类,其中包含一些方法,这些方法将作为连接点供我们进行增强操作。
 

// 被增强的类

public class User {

//连接点/切入点

public void add(){

System.out.println("add......");

}

public void update(){

System.out.println("update......");

}

}

  1. 将目标类配置到 Spring 中:在 Spring 的配置文件(applicationContext.xml)中,将 User 类配置为一个 Bean,以便 Spring 容器进行管理。
 

<bean id="user" class="com.aopImpl.User"></bean>

  1. 定义切面类:创建一个切面类,例如 UserProxy 类,在该类中定义各种通知方法。比如,定义一个前置通知方法 before ()。
 

public class UserProxy {

//增强/通知 ---》前置通知

public void before(){

System.out.println("before.............");

}

}

  1. 在配置文件中定义切面类:同样在 Spring 配置文件中,将 UserProxy 类也配置为一个 Bean。
 

<bean id="userProxy" class="com.aopImpl.UserProxy"></bean>

  1. 在配置文件中完成 aop 的配置:最后,在配置文件中进行 AOP 的核心配置。通过<aop:config>标签来配置切面,将切入点和通知关联起来。例如,配置一个前置通知,在 User 类的 add () 方法执行前进行增强。
 

<!--配置切面-->

<aop:config>

<!--配置切面 = 切入点 + 通知组成-->

<aop:aspect ref="userProxy">

<!--前置通知:UserServiceImpl的save方法执行前,会增强-->

<!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强-->

<aop:before method="before" pointcut="execution(public void com.aopImpl.User.add())"/>

</aop:aspect>

</aop:config>

  1. 完成测试:编写测试类,通过 Spring 的 ApplicationContext 获取 User 类的实例,并调用其 add () 方法,观察前置通知是否生效。
 

public class DemoTest {

@Test

public void aopTest1(){

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

User user = (User) applicationContext.getBean("user");

user.add();

}

}

(四)切入点的表达式

在配置切入点时,需要使用切入点表达式来精确指定要增强的方法。切入点表达式的格式如下:

 

execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])

  • 修饰符:可以省略不写,不是必须出现的部分。
  • 返回值类型:不能省略,需要根据实际方法的返回值类型编写,也可以使用*代替所有返回值类型。
  • 包名、类名、方法名和参数的规则
    • 包名、类名和方法名通常不能省略,但可以使用*进行通配。例如,com.qcby.demo3.BookDaoImpl.save(),可以使用*来简化表示,如com.qcby.*.BookDaoImpl.save()表示com.qcby包下所有以BookDaoImpl结尾的类中的save方法。
    • 中间的包名可以使用*号代替,类名也可以使用*号代替,还可以使用类似*DaoImpl的写法。方法同样可以使用*号代替。
    • 参数部分,如果是一个参数可以使用*号代替,如果想代表任意参数则使用..。

以下是一些常见的切入点表达式示例:

  • 比较通用的表达式:execution(* com.qcby.*.ServiceImpl.save(..)),表示对com.qcby包下所有以ServiceImpl结尾的类中的save方法进行增强,且方法可以接受任意类型和数量的参数。
  • 对某个类中所有方法进行增强:execution(* com.qcby.*.ServiceImpl.*(..)),表示对com.qcby包下所有以ServiceImpl结尾的类中的所有方法进行增强,方法接受任意参数。
  • 对某个包中所有方法进行增强:execution(* com.qcby.*.*.*(..)),表示对com.qcby包及其子包下的所有类中的所有方法进行增强,方法接受任意参数。

(五)AOP 的通知类型

  1. 前置通知:在目标方法执行前进行增强。如上述配置案例中,在 User 类的 add () 方法执行前,会执行 before () 方法中的逻辑。
  1. 环绕通知:在目标方法执行前后都可以进行增强。需要注意的是,在环绕通知方法中,目标对象的方法需要手动调用。例如:
 

// 环绕通知

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

System.out.println("before.............");

// 执行被增强的方法

proceedingJoinPoint.proceed();

System.out.println("after.............");

}

在 xml 配置中,使用<aop:around>标签来配置环绕通知:

 

<aop:around method="around" pointcut="execution(* com.*.User.add(..))"/>

  1. 最终通知:无论目标方法执行成功还是失败,都会进行增强。例如:
 

// 最终通知

public void after() {

System.out.println("after.............");

}

xml 配置如下:

 

<aop:after method="after" pointcut="execution(* com.*.User.add(..))"/>

  1. 后置通知:在目标方法执行成功后进行增强。例如:
 

//后置通知

public void afterReturning() {

System.out.println("afterReturning.............");

}

xml 配置:

 

<aop:after-returning method="afterReturning" pointcut="execution(public void com.aopImpl.User.add())"/>

  1. 异常通知:在目标方法执行失败后进行增强,只有当目标方法发生异常时才会执行。例如:
 

//异常通知

public void afterThrowing() {

System.out.println("afterThrowing.............");

}

需要注意的是,为了触发异常通知,需要在目标方法中故意制造异常。例如:

 

//连接点/切入点

public void add(){

int a = 10 / 0;

System.out.println("add......");

}

xml 配置:

 

<aop:after-throwing method="afterThrowing" pointcut="execution(public void com.aopImpl.User.add())"/>

五、Spring 的 AOP 技术 - 注解方式

(一)AOP 注解方式入门程序

  1. 创建 maven 工程,导入坐标:与配置文件方式类似,首先创建一个 maven 项目,并导入相关的依赖坐标,包括 Spring 的核心依赖、AOP 相关依赖等。
  2. 编写接口,完成 IOC 的操作:根据实际业务需求,编写相关的接口,并通过 Spring 的 IOC 机制进行配置和管理,这里步骤略。
  3. 编写切面类:创建一个切面类,例如 UserProxy 类。给切面类添加@Aspect注解,表明该类是一个切面。然后在类中编写各种增强方法,并使用相应的通知类型注解来声明。
  4. 配置 xml 扫描注解:在 Spring 的配置文件(applicationContext.xml)中,配置注解扫描,确保 Spring 容器能够识别并处理这些注解。同时,需要引入相关的命名空间。

5.配置文件中开启自动代理:在 Spring 配置文件中,通过<aop:aspectj-autoproxy>标签开启 AspectJ 自动代理功能,使得 Spring 能够基于注解创建代理对象来实现 AOP 功能。完整的配置文件如下:

 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--开启注解扫描-->

<context:component-scan base-package="com.aopImpl"></context:component-scan>

<!--开启Aspect生成代理对象-->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

  1. 通知类型注解:在切面类中,使用不同的通知类型注解来定义增强逻辑。
    • @Before -- 前置通知:用于在目标方法执行前执行增强逻辑。例如:
 

@Component

@Aspect //生成代理对象

public class UserProxy {

//增强/通知 ---》前置通知

@Before(value = "execution(* com.*.User.add(..))")

public void before(){

System.out.println("before.............");

}

}

  • @AfterReturning -- 后置通知:在目标方法成功执行返回后执行增强逻辑。
 

//后置通知

@AfterReturning(value = "execution(* com.*.User.add(..))")

public void afterReturning() {

System.out.println("afterReturning.............");

}

  • @Around -- 环绕通知:可以在目标方法执行前后都执行增强逻辑,且目标对象的方法默认不执行,需要手动通过ProceedingJoinPoint的proceed()方法来触发。
 

// 环绕通知

@Around(value = "execution(* com.*.User.add(..))")

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

System.out.println("before.............");

// 执行被增强的方法

proceedingJoinPoint.proceed();

System.out.println("after.............");

}

  • @After -- 最终通知:无论目标方法执行成功与否,都会执行该通知的增强逻辑。
 

// 最终通知

@After(value = "execution(* com.*.User.add(..))")

public void after() {

System.out.println("after.............");

}

  • @AfterThrowing -- 异常抛出通知:当目标方法抛出异常时执行增强逻辑。
 

//异常通知

@AfterThrowing(value = "execution(* com.*.User.add(..))")

public void afterThrowing() {

System.out.println("afterThrowing.............");

}

  1. 测试类:编写测试类来验证 AOP 注解配置是否生效。通过ApplicationContext获取被增强的对象,并调用其方法,观察通知是否按照预期执行。
 

@Test

public void aopTest1(){

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

User user = applicationContext.getBean(User.class);

user.add();

}

六、AOP 在实际项目中的应用场景

(一)日志记录

在企业级应用中,日志记录是非常重要的功能。通过 AOP,我们可以在不修改业务逻辑代码的情况下,为所有需要记录日志的方法添加日志记录功能。例如,在方法执行前记录方法的开始时间和入参,在方法执行后记录方法的结束时间和返回值。这样可以方便我们进行系统调试、性能分析以及问题排查。

 

@Component

@Aspect

public class LoggingAspect {

@Before("execution(* com.example.service.*.*(..))")

public void logBefore(JoinPoint joinPoint) {

System.out.println("开始执行方法:" + joinPoint.getSignature().getName());

System.out.println("入参:" + Arrays.toString(joinPoint.getArgs()));

}

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")

public void logAfterReturning(JoinPoint joinPoint, Object result) {

System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行成功,返回值:" + result);

}

}

(二)事务管理

事务管理是保证数据一致性和完整性的关键。使用 AOP 可以将事务管理的逻辑从业务代码中分离出来。通过配置事务切面,在方法执行前开启事务,在方法执行成功后提交事务,在方法抛出异常时回滚事务。

 

<tx:advice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="save*" propagation="REQUIRED"/>

<tx:method name="update*" propagation="REQUIRED"/>

<tx:method name="delete*" propagation="REQUIRED"/>

<tx:method name="*" read-only="true"/>

</tx:attributes>

</tx:advice>

<aop:config>

<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>

<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>

</aop:config>

(三)权限校验

在系统中,不同的用户角色可能具有不同的操作权限。利用 AOP 可以在方法调用前进行权限校验,确保只有具有相应权限的用户才能执行特定的方法。例如,在一个电商系统中,只有管理员用户才能执行商品删除操作。

 

@Component

@Aspect

public class PermissionAspect {

@Before("execution(* com.example.controller.AdminController.deleteProduct(..))")

public void checkPermission(JoinPoint joinPoint) {

// 假设通过SecurityContext获取当前用户角色

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

if (!authorities.contains(new SimpleGrantedAuthority("ADMIN"))) {

throw new AccessDeniedException("没有权限执行该操作");

}

}

}

七、总结

Spring 框架中的 AOP 技术为开发者提供了一种强大而灵活的编程方式,能够有效地将通用功能与业务逻辑分离,提高代码的可维护性、可重用性和开发效率。通过配置文件方式和注解方式,我们可以轻松地实现 AOP 功能,包括定义切入点、通知和切面。在实际项目中,AOP 在日志记录、事务管理、权限校验等多个方面都有着广泛的应用。深入理解和熟练掌握 AOP 技术,将有助于我们构建更加健壮、高效的 Java 企业级应用。希望本文能够帮助读者全面掌握 Spring 框架中 AOP 技术的精髓,并在实际开发中灵活运用,提升项目的质量和开发效率。

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

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

相关文章

磁盘清理工具-TreeSize Free介绍

TreeSizeFree是一个磁盘空间管理工具&#xff0c;主要用于分析磁盘使用情况&#xff0c;帮助用户找到占用空间大的文件和文件夹: 特点&#xff1a;按大小排序&#xff1a;快速找到占用空间最大的文件或文件夹 一般可以删除: 扫描 C:\Users\XXX\AppData\Local\Temp 或 C:\Window…

c#知识点补充2

1.非静态类能否调用静态方法可以 2.对string类型扩展方法&#xff0c;如何进行 类用静态类&#xff0c;参数是this 调用如下 3.out的用法 一定要给a赋值 这种写法不行 这样才行 4.匿名类 5.委托的使用 无论是匿名委托&#xff0c;还是具命委托&#xff0c;委托实例化后一定要…

力扣45.跳跃游戏

45. 跳跃游戏 II - 力扣&#xff08;LeetCode&#xff09; 代码区&#xff1a; #include<vector> class Solution {public:int jump(vector<int>& nums) {int ans[10005] ;memset(ans,1e4,sizeof(ans));ans[0]0;for(int i0;i<nums.size();i){for(int j1;j…

MacOS安装 nextcloud 的 Virtual File System

需求 在Mac上安装next cloud实现类似 OneDrive 那样&#xff0c;文件直接保存在服务器&#xff0c;需要再下载到本地。 方法 在 官网下载Download for desktop&#xff0c;注意要下对版本&#xff0c;千万别下 Mac OS默认的那个。 安装了登录在配置过程中千万不要设置任何同…

C/C++蓝桥杯算法真题打卡(Day6)

一、P8615 [蓝桥杯 2014 国 C] 拼接平方数 - 洛谷 方法一&#xff1a;算法代码&#xff08;字符串分割法&#xff09; #include<bits/stdc.h> // 包含标准库中的所有头文件&#xff0c;方便编程 using namespace std; // 使用标准命名空间&#xff0c;避免每次调用…

ORACLE RAC ASM双存储架构下存储部分LUN异常的处理

早上接到用户电话&#xff0c;出现有表空间不足的告警&#xff0c;事实上此环境经常巡检并且有告警系统&#xff0c;一开始就带着有所疑惑的心理&#xff0c;结果同事在扩大表空间时&#xff0c;遇到报错 ORA-15401/ORA-17505,提示ASM空间满了&#xff1a; ALERT日志&#xff1…

others-rustdesk远程

title: others-rustdesk远程 categories: Others tags: [others, 远程] date: 2025-03-19 10:19:34 comments: false mathjax: true toc: true others-rustdesk远程, 替代 todesk 的解决方案 前篇 官方 服务器 - https://rustdesk.com/docs/zh-cn/self-host/rustdesk-server-o…

C++基础 [八] - list的使用与模拟实现

目录 list的介绍 List的迭代器失效问题 List中sort的效率测试 list 容器的模拟实现思想 模块分析 作用分析 list_node类设计 list 的迭代器类设计 迭代器类--存在的意义 迭代器类--模拟实现 模板参数 和 成员变量 构造函数 * 运算符的重载 运算符的重载 -- 运…

使用excel.EasyExcel实现导出有自定义样式模板的excel数据文件,粘贴即用!!!

客户要求导出的excel文件是有好看格式的&#xff0c;当然本文举例模板文件比较简单&#xff0c;内容丰富的模板可以自行设置&#xff0c;话不多说&#xff0c;第一步设置一个"好看"的excel文件模板 上面要注意的地方是{.变量名} &#xff0c;这里的变量名对应的就是…

Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务

解释 在spingboot 集成es客户端后&#xff0c;每当服务启动时&#xff0c;服务默认都会查看es中是否已经创建了对应的索引&#xff0c;如果没有索引则创建。基于上面的规则我们可以通过配置不自动创建索引来达到在没有es服务的情况下正常启动服务。 解决办法 在entity类的Docu…

JVM常见概念之条件移动

问题 当我们有分支频率数据时&#xff0c;有什么有趣的技巧可以做吗&#xff1f;什么是条件移动&#xff1f; 基础知识 如果您需要在来自一个分支的两个结果之间进行选择&#xff0c;那么您可以在 ISA 级别做两件不同的事情。 首先&#xff0c;你可以创建一个分支&#xff…

Android AI ChatBot-v1.6.3-28-开心版[免登录使用GPT-4o和DeepSeek]

Android AI ChatBot- 链接&#xff1a;https://pan.xunlei.com/s/VOLi1Ua071S6QZBGixcVL5eeA1?pwdp3tt# 免登录使用GPT-4o和DeepSeek

集成学习(上):Bagging集成方法

一、什么是集成学习&#xff1f; 在机器学习的世界里&#xff0c;没有哪个模型是完美无缺的。就像古希腊神话中的"盲人摸象"&#xff0c;单个模型往往只能捕捉到数据特征的某个侧面。但当我们把多个模型的智慧集合起来&#xff0c;就能像拼图一样还原出完整的真相&a…

DeepSeek R1 本地部署指南 (3) - 更换本地部署模型 Windows/macOS 通用

0.准备 完成 Windows 或 macOS 安装&#xff1a; DeepSeek R1 本地部署指南 (1) - Windows 本地部署-CSDN博客 DeepSeek R1 本地部署指南 (2) - macOS 本地部署-CSDN博客 以下内容 Windows 和 macOS 命令执行相同&#xff1a; Windows 管理员启动&#xff1a;命令提示符 CMD ma…

【TI MSPM0】Timer学习

一、计数器 加法计数器&#xff1a;每进入一个脉冲&#xff0c;就加一减法计算器&#xff1a;每进入一个脉冲&#xff0c;就减一 当计数器减到0&#xff0c;触发中断 1.最短计时时间 当时钟周期为1khz时&#xff0c;最短计时时间为1ms&#xff0c;最长计时时间为65535ms 当时…

Windows部署deepseek R1训练数据后通过AnythingLLM当服务器创建问答页面

如果要了解Windows部署Ollama 、deepseek R1请看我上一篇内容。 这是接上一篇的。 AnythingLLM是一个开源的全栈AI客户端&#xff0c;支持本地部署和API集成。它可以将任何文档或内容转化为上下文&#xff0c;供各种语言模型&#xff08;LLM&#xff09;在对话中使用。以下是…

信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗

信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗 题目背景 NOIP2018 普及组 T2 题目描述 轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n n n 个兵营(自左至右编号 1 ∼ n 1 \sim n 1∼n),相邻编号的兵营之间…

多模态大模型常见问题

1.视觉编码器和 LLM 连接时&#xff0c;使用 BLIP2中 Q-Former那种复杂的 Adaptor 好还是 LLaVA中简单的 MLP 好&#xff0c;说说各自的优缺点&#xff1f; Q-Former&#xff08;BLIP2&#xff09;&#xff1a; 优点&#xff1a;Q-Former 通过查询机制有效融合了视觉和语言特征…

SpringBoot项目实战(初级)

目录 一、数据库搭建 二、代码开发 1.pom.xml 2.thymeleaf模块处理的配置类 3.application配置文件 4.配置&#xff08;在启动类中&#xff09; 5.编写数据层 ②编写dao层 ③编写service层 接口 实现类 注意 补充&#xff08;注入的3个注解&#xff09; 1.AutoWir…

计算机网络——总结

01. 网络的发展及体系结构 网络演进历程 从1969年ARPANET的4个节点发展到如今覆盖全球的互联网&#xff0c;网络技术经历了电路交换到分组交换、有线连接到无线覆盖的革命性变革。5G时代的到来使得网络传输速度突破10Gbps&#xff0c;物联网设备数量突破百亿级别。 网络体系…