当前位置: 首页 > news >正文

使用Spring的AOP

使用Spring的AOP

  • 一、AOP 的常用注解
    • 1.切面类@Aspect
    • 2.@Pointcut
    • 3.前置通知@Before
    • 4.后置通知@AfterReturning
    • 5.环绕通知@Around
    • 6.异常通知@AfterThrowing
    • 7.最终通知@After
    • 8.切面顺序@Order
    • 9.启用自动代理@EnableAspectJAutoProxy
  • 二、AOP注解方式开发
  • 三、AOP 全注解开发
  • 四、基于XML配置方式的AOP(了解)


  • Spring 对 AOP 的实现包括以下3种方式:
    • 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
    • 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
    • 第三种方式:Spring框架自己实现的AOP,基于XML方式。
  • 实际开发种都是Spring + AspectJ来实现的AOP。
  • 什么是AspectJ?(Eclipse组织的一个支持AOP的框架。AspectJ框架是独立于Spring框架之外的一个框架,Spring框架用了AspectJ)
  • AspectJ项目起源于帕洛阿尔托(Palo Alto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregor Kiczales领导,从1997年开始致力于AspectJ的开发,1998年第一次发布给外部用户,2001年发布1.0 release。为了推动AspectJ技术和社团的发展,PARC在2003年3月正式将AspectJ项目移交给了Eclipse组织,因为AspectJ的发展和受关注程度大大超出了PARC的预期,他们已经无力继续维持它的发展。

一、AOP 的常用注解

1.切面类@Aspect

  • @Aspect作用是把当前类标识为一个切面供容器读取。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface Aspect {String value() default "";
    }
    

2.@Pointcut

  • @Pointcut注解标注在方法上面,用来定义切入点。
  • 可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Pointcut {String value() default "";String argNames() default "";
    }
    

3.前置通知@Before

  • @Before目标方法执行之前的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Before {String value();String argNames() default "";
    }
    

4.后置通知@AfterReturning

  • @AfterReturning目标方法执行之后的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterReturning {String value() default "";String pointcut() default "";String returning() default "";String argNames() default "";
    }
    

5.环绕通知@Around

  • @Around目标方法之前添加通知,同时目标方法执行之后添加通知。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Around {String value();String argNames() default "";
    }
    

6.异常通知@AfterThrowing

  • @AfterThrowing发生异常之后执行的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterThrowing {String value() default "";String pointcut() default "";String throwing() default "";String argNames() default "";
    }
    

7.最终通知@After

  • @After放在finally语句块中的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface After {String value();String argNames() default "";
    }
    

8.切面顺序@Order

  • 我们知道,业务流程当中不一定只有一个切面,可能有的切面控制事务,有的记录日志,有的进行安全控制,如果多个切面的话,顺序如何控制:可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {/*** The order value.* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.* @see Ordered#getOrder()*/int value() default Ordered.LOWEST_PRECEDENCE;
    }
    

9.启用自动代理@EnableAspectJAutoProxy

  • 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false;
    }
    

二、AOP注解方式开发

  • 注意:本文使用了 log4j2 日志,如果不知道可以看我的博客 ===> Spring对IoC的实现中的第一个Spring程序

  • 注意本文也使用了 junit 进行单元测试。

  • 使用Spring+AspectJAOP需要引入的依赖如下:

    <!--spring的核心依赖 aop core beans jcl expression 等-->
    <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.4</version>
    </dependency>
    <!-- AOP 依赖的AspectJ -->
    <dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.4</version>
    </dependency>
    
  • Spring配置文件中添加context命名空间和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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"></beans>
    
  • 第一步:定义目标类以及目标方法

    package com.gdb.service;import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;//目标类
    @Service
    public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);//目标方法public void detail() {logger.info("正在打印订单详情......");}
    }
    
  • 第二步:编写切面类

    package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;//切面类(通知+切点 = 切面)
    @Aspect
    @Component
    public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);//这是需要增强的代码(通知)@Before("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed(); // 执行目标方法logger.info("后置环绕通知执行了");}@AfterThrowing("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");}
    }
    
  • 第三步:在配置文件中启动包扫描启用自动代理

    <?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="com.gdb"/><!--开启自动代理--><aop:aspectj-autoproxy proxy-target-class="false"/><!--<aop:aspectj-autoproxy  proxy-target-class="true"/> 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。proxy-target-class="true" 表示采用cglib动态代理。proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。-->
    </beans>
    
  • 第四步:编写测试程序

    @Test
    public void test(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
    }
    
  • 第五步:执行结果
    在这里插入图片描述

    • 通过上面的执行结果就可以判断他们的执行顺序了,这里不再赘述。
  • 第六步:结果中没有异常通知,这是因为目标程序执行过程中没有发生异常。我们尝试让目标方法发生异常:

    package com.gdb.service;import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;@Service
    public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");throw new RuntimeException();}
    }
    
  • 第七步:执行结果:
    在这里插入图片描述

    • 通过测试得知,当发生异常之后,最终通知也会执行,因为最终通知@After会出现在finally语句块中。出现异常之后,后置通知环绕通知的结束部分不会执行。
  • 优化使用切点表达式:

    • 上面编写的切面类的缺点是:
      • 第一:切点表达式重复写了多次,没有得到复用。
      • 第二:如果要修改切点表达式,需要修改多处,难维护。
  • 可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。

    package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;@Aspect
    @Component
    public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);@Pointcut("execution(* com.gdb.service..* (..))")public void pointcut() {}@Before("pointcut()") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("pointcut()") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("pointcut()") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}@AfterThrowing("pointcut()") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("pointcut()") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");}
    }
    
  • 使用@Pointcut注解来定义独立的切点表达式。

  • 注意这个@Pointcut注解标注的方法随意,只是起到一个能够让@Pointcut注解编写的位置。


三、AOP 全注解开发

  • 第一步:就是编写一个类,在这个类上面使用大量注解来代替spring的配置文件,spring配置文件消失了,如下:
    package com.gdb.config;import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration // 配置类
    @ComponentScan("com.gdb")
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    public class SpringConfig {
    }
    
  • 第二步:测试程序也变化了
    @Test
    public void test() {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
    }
    
  • 第三步:执行结果
    在这里插入图片描述

四、基于XML配置方式的AOP(了解)

  • 第一步:编写目标类
package com.gdb.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");}
}
  • 第二步:编写切面类,并且编写通知
package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);public void beforeAdvice() {logger.info("前置通知执行了");}public void afterReturningAdvice() {logger.info("后置通知执行了");}public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}public void afterThrowingAdvice() {logger.info("异常通知执行了");}public void afterAdvice() {logger.info("最终通知执行了");}
}
  • 第三步:编写spring配置文件
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="myAspect" class="com.gdb.aspect.MyAspect"/><bean id="orderService" class="com.gdb.service.OrderService"/><!--aop配置--><aop:config><!--切点表达式--><aop:pointcut id="p" expression="execution(* com.gdb.service..* (..))"/><!--切面--><aop:aspect ref="myAspect"><!--切面=通知 + 切点--><aop:before method="beforeAdvice" pointcut-ref="p"/><aop:after-returning method="afterReturningAdvice" pointcut-ref="p"/><aop:around method="aroundAdvice" pointcut-ref="p"/><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="p"/><aop:after method="afterAdvice" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>
  • 第四步:编写测试程序
@Test
public void test() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
}
  • 第四步:执行结果
    在这里插入图片描述
  • 通过结果可以看出来顺序和前面的不一样了,我感觉是配置文件中的顺序有关系,由于主要都是使用注解的方式。

相关文章:

使用Spring的AOP

使用Spring的AOP 一、AOP 的常用注解1.切面类Aspect2.Pointcut3.前置通知Before4.后置通知AfterReturning5.环绕通知Around6.异常通知AfterThrowing7.最终通知After8.切面顺序Order9.启用自动代理EnableAspectJAutoProxy 二、AOP注解方式开发三、AOP 全注解开发四、基于XML配置…...

爬虫之矛---JavaScript基石篇3<JavaScript构造函数的内部机制和应用(2)>

前言: 继续上一篇https://blog.csdn.net/m0_56758840/article/details/136592611 正文: 1.ES6中的类和构造函数的对应关系 A. 介绍ES6引入的类的概念和语法糖 类的概念&#xff1a; ES6引入了类&#xff08;class&#xff09;的概念&#xff0c;类是一种抽象的数据类型&…...

_note_05

1.说一说什么是函数重载&#xff1f; 函数签名相同除了 形参不同数据类型 函数签名相同除了 形参不同个数 2.void关键字的作用&#xff1f;返回值是void &#xff0c;可以写return 吗&#xff1f; 函数无返回&#xff0c;使用void修饰; 可以只使用return使函数结束; 3.按要…...

将格蠹GDK8的cmake3.10升级为cmake3.15

#升级过程# 1、wget https://cmake.org/files/v3.15/cmake-3.15.0-rc1.tar.gz 2、tar -zxvf cmake-3.15.0-rc1.tar.gz 3 、cd cmake-3.15.0-rc1 4、./configure 5、sudo make install 6、reboot 7、查看cmake版本&#xff1a; geduergdk8:~$ cmake --version cmake ve…...

b树(一篇文章带你 理解 )

目录 一、引言 二、B树的基本定义 三、B树的性质与操作 1 查找操作 2 插入操作 3 删除操作 四、B树的应用场景 1 数据库索引 2 文件系统 3 网络路由表 五、哪些数据库系统不使用B树进行索引 1 列式数据库 2 图形数据库 3 内存数据库 4 NoSQL数据库 5 分布式数据…...

OD_2024_C卷_200分_7、5G网络建设【JAVA】【最小生成树】

package odjava;import java.util.Scanner;public class 七_5G网络建设 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 基站数量&#xff08;节点数&#xff09;int m sc.nextInt(); // 基站对数量&#xff08;边数&…...

面试题:分布式锁用了 Redis 的什么数据结构

在使用 Redis 实现分布式锁时&#xff0c;通常使用 Redis 的字符串&#xff08;String&#xff09;。Redis 的字符串是最基本的数据类型&#xff0c;一个键对应一个值&#xff0c;它能够存储任何形式的字符串&#xff0c;包括二进制数据。字符串类型的值最多可以是 512MB。 Re…...

【学习心得】websocket协议简介并与http协议对比

一、轮询和长轮询 在websocket协议出现之前&#xff0c;要想实现服务器和客户端的双向持久通信采取的是Ajax轮询。它的原理是每隔一段时间客户端就给服务器发送请求找服务器要数据。 让我们通过一个生活化的比喻来解释轮询和长轮询假设你正在与一位不怎么主动说话的老大爷&…...

基于Token的身份验证:安全与效率的结合

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

Electron程序如何在MacOS下获取相册访问权限

1.通过entitiment.plist&#xff0c;在electron-builder签名打包时&#xff0c;给app包打上签名。最后可以通过codesign命令进行验证。 TestPhotos.plist electron-builder配置文件中加上刚刚的plist文件。 通过codesign命令验证&#xff0c;若出现这个&#xff0c;则说明成…...

uniapp让输入框保持聚焦状态,不会失去焦点

使用场景&#xff1a;当输入框还有发送按钮的时候&#xff0c;点击发送希望软键盘不消失&#xff0c;还可以继续输入&#xff0c;或者避免因输入图片标签造成的屏闪问题 多次尝试后发现一个很实用的方法&#xff0c;适用input输入框和editor输入框 解决办法&#xff1a;把cli…...

面试中如何介绍mysql的B+树

B树是B树的变体&#xff0c;也是一颗多路搜索树。在MySQL中&#xff0c;B树是为磁盘或者其他直接辅助存储设备所设计的一种平衡的查找树结构。其具有以下特点&#xff1a; 每个节点最多有m个子女&#xff0c;m阶的B树深度最多为m。非根节点关键值个数范围是⌈m/2⌉-1<k<m…...

【Linux C | 网络编程】多播的概念、多播地址、UDP实现多播的C语言例子

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

AIGC实战——GPT(Generative Pre-trained Transformer)

AIGC实战——GPT 0. 前言1. GPT 简介2. 葡萄酒评论数据集3. 注意力机制3.1 查询、键和值3.2 多头注意力3.3 因果掩码 4. Transformer4.1 Transformer 块4.2 位置编码 5. 训练GPT6. GPT 分析6.1 生成文本6.2 注意力分数 小结系列链接 0. 前言 注意力机制能够用于构建先进的文本…...

微信小程序-入门

一.通过 Npm方式下载构建 1.下载和安装Npm&#xff1a;Npm https://docs.npmjs.com/downloading-and-installing-node-js-and-npm 或者 https://nodejs.org/en/download/ 未安装npm 提示 以下以安装node安装包为例 按任意键继续 安装完成后 2. 下载和安装小程序开…...

0102全排列和对换-行列式-线性代数

把n个不同的数排成一列&#xff0c;叫做这n个数的全排列&#xff08;排列&#xff09;。 一般情况&#xff0c; 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n是n个数排列的标准次序。 当n个数的任一排列中两个数的先后次序与标准次序不同时&#xff0c;有说有一个逆序。 一个排列中所…...

面向对象的编程语言是什么意思?——跟老吕学Python编程

面向对象的编程语言是什么意思&#xff1f;——跟老吕学Python编程 面向对象是什么意思&#xff1f;面向对象的定义面向对象的早期发展面向对象的背景1.审视问题域的视角2.抽象级别3.封装体4.可重用性 面向对象的特征面向对象的开发方法面向对象程序设计基本思想实现 面向对象的…...

Spring Boot整合MyBatis Plus配置多数据源

Spring Boot 专栏&#xff1a;https://blog.csdn.net/dkbnull/category_9278145.html Spring Cloud 专栏&#xff1a;https://blog.csdn.net/dkbnull/category_9287932.html GitHub&#xff1a;https://github.com/dkbnull/SpringBootDemo Gitee&#xff1a;https://gitee.com/…...

Unix Network Programming Episode 88

‘inetd’ Daemon On a typical Unix system, there could be many servers in existence, just waiting for a client request to arrive. Examples are FTP, Telnet, Rlogin, TFTP, and so on. With systems before 4.3BSD, each of these services had a process associate…...

Java面试题之11MySQL

你对MySQL执行计划怎么看 执行计划就是SQL的执行查询的顺序&#xff0c;以及如何使用索引查询&#xff0c;返回的结果集的行数 在MySQL中&#xff0c;我们可以通过explain命令来查看执行计划。其语法如下&#xff1a; EXPLAIN SELECT * FROM table_name WHERE conditions;在…...

Qwen-Image-Edit快速上手:基于深度显存优化,普通显卡也能流畅运行

Qwen-Image-Edit快速上手&#xff1a;基于深度显存优化&#xff0c;普通显卡也能流畅运行 1. 项目概述 Qwen-Image-Edit是由阿里通义千问团队开源的一款创新性图像编辑工具&#xff0c;通过深度显存优化技术&#xff0c;让普通显卡也能流畅运行高质量的AI图像编辑。这个工具最…...

Open NSynth Super软件架构:openFrameworks音频应用深度剖析

Open NSynth Super软件架构&#xff1a;openFrameworks音频应用深度剖析 【免费下载链接】open-nsynth-super Open NSynth Super is an experimental physical interface for the NSynth algorithm 项目地址: https://gitcode.com/gh_mirrors/op/open-nsynth-super Open…...

为什么你的vLLM吞吐量比竞品低37%?,深度拆解基准测试套件中被忽略的3层时序对齐机制(含CUDA Graph级采样代码)

第一章&#xff1a;大模型工程化性能基准测试套件的范式演进 2026奇点智能技术大会(https://ml-summit.org) 大模型工程化已从单点推理验证迈入全栈协同优化阶段&#xff0c;性能基准测试套件不再仅关注吞吐量与延迟&#xff0c;而是系统性覆盖模型编译、显存调度、分布式通信…...

Rust的匹配中的通配符模式与剩余模式在元组解构中的组合使用技巧

Rust作为一门注重安全与性能的系统级编程语言&#xff0c;其模式匹配机制为开发者提供了强大的表达能力。在元组解构中&#xff0c;通配符模式与剩余模式的组合使用尤其值得关注&#xff0c;它们能显著提升代码的简洁性与灵活性。本文将深入探讨这一技巧的实用场景&#xff0c;…...

DX-BT24蓝牙模块实战:从AT指令到手机透传的完整指南

1. 认识DX-BT24蓝牙模块 第一次拿到DX-BT24蓝牙模块时&#xff0c;我完全被它的小巧震惊了——只有拇指大小的板子&#xff0c;居然能实现完整的蓝牙5.1通信功能。这个由大夏龙雀科技推出的模块&#xff0c;最大的特点就是内置了标准串口协议&#xff0c;让开发者可以像操作普通…...

深度解析AI Agent的工具调用机制:注册发现、动态选择与执行链路设计

深度解析AI Agent的工具调用机制:注册发现、动态选择与执行链路设计 关键词 AI Agent, 工具调用, 注册发现, 动态选择, 执行链路, LLM, 函数调用 摘要 随着大型语言模型(LLM)的快速发展,AI Agent作为一种能够自主完成复杂任务的智能体正日益受到关注。本文将深度解析AI A…...

LP8 CO₂传感器Arduino库详解:MODBUS-RTU通信与NDIR数据处理

1. LP8 CO₂传感器Arduino库深度解析与工程实践指南1.1 库定位与核心价值LP8 CO₂传感器Arduino库是一个面向嵌入式环境的轻量级、高可靠性MODBUS-RTU通信封装库&#xff0c;专为意法半导体&#xff08;STMicroelectronics&#xff09;旗下LP8系列非分散红外&#xff08;NDIR&a…...

电容是什么?一个“快充快放”的微型充电宝乐

一、前言&#xff1a;什么是 OFA VQA 模型&#xff1f; OFA&#xff08;One For All&#xff09;是字节跳动提出的多模态预训练模型&#xff0c;支持视觉问答、图像描述、图像编辑等多种任务&#xff0c;其中视觉问答&#xff08;VQA&#xff09;是最常用的功能之一——输入一张…...

Arduino_CloudUtils:嵌入式物联网云通信核心工具库

1. Arduino_CloudUtils 库深度解析&#xff1a;嵌入式云通信核心工具链Arduino_CloudUtils 是 Arduino 官方为物联网云连接场景设计的底层通用工具库&#xff0c;其定位并非独立应用框架&#xff0c;而是作为 ArduinoIoTCloud 等上层云 SDK 的“基础设施层”。该库不处理网络协…...

别再一关了之!手把手教你用setenforce命令调试SELinux权限问题(附安卓init流程解析)

SELinux调试实战&#xff1a;从权限拒绝到策略优化的完整指南 遇到SELinux权限问题时&#xff0c;很多开发者第一反应是直接关闭它——这就像因为门锁太复杂而直接把大门拆掉。本文将带你深入理解SELinux的工作机制&#xff0c;并掌握一套系统化的调试方法&#xff0c;让你既能…...