SSM框架,spring-aop的学习
代理模式
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。
可以将重复的非核心的代码写在代理类中,将核心的代码写在目标方法中,每次通过调用代理类的方法来间接调用目标方法,以方便统一管理重复的非核心的代码。
静态代理
每个目标方法都有其对应的代理类中的代理方法,重复的非核心的代码都写在代理类中,例:
public class StaticProxyCalculator implements Calculator {private Calculator calculator;public StaticProxyCalculator(Calculator calculator) {this.calculator = calculator;}@Overridepublic int add(int i, int j) {System.out.println("i = " + i + ", j = " + j);int result = calculator.add(i,j);System.out.println("result = " + result);return result;}@Overridepublic int sub(int i, int j) {System.out.println("i = " + i + ", j = " + j);int result = calculator.sub(i,j);System.out.println("result = " + result);return result;}@Overridepublic int mul(int i, int j) {System.out.println("i = " + i + ", j = " + j);int result = calculator.mul(i,j);return result;}@Overridepublic int div(int i, int j) {System.out.println("i = " + i + ", j = " + j);int result = calculator.div(i,j);System.out.println("result = " + result);return 0;}
}
但是这种方法还是有大量重复,也没有很好地解决统一管理的问题,也不具备灵活性,所以要使用动态代理的方法
动态代理
动态代理技术分类
- JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口,他会根据目标类的接口动态生成一个代理对象,代理对象和目标对象有相同的接口。
- cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。
以jdk代理为例(了解即可)
代理类:
public class ProxyFactory {private Object target;public ProxyFactory(Object target) {this.target = target;}public Object getProxy(){/*** newProxyInstance():创建一个代理实例* 其中有三个参数:* 1、classLoader:加载动态生成的代理类的类加载器* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法*/ClassLoader classLoader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** proxy:代理对象* method:代理对象需要实现的方法,即其中需要重写的方法* args:method所对应方法的参数*/Object result = null;try {System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));result = method.invoke(target, args);System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);} catch (Exception e) {e.printStackTrace();System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());} finally {System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");}return result;}};return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);}
}
public class Test {/*** 静态代理*/@org.junit.jupiter.api.Testpublic void test1(){Calculator target = new CalculatorPureImpl();Calculator calculator = new StaticProxyCalculator(target);calculator.add(3,4);}/*** 动态代理*/@org.junit.jupiter.api.Testpublic void test2(){//创建目标对象Calculator target = new CalculatorPureImpl();//获取代理对象ProxyFactory factory = new ProxyFactory(target);Calculator proxy = (Calculator) factory.getProxy();proxy.add(3,4);}
}
不需要编写代理代码,我们可以使用Spring AOP实现代理
面向切面编程(AOP)
面向切面编程(Aspect Oriented Programming)是对面向对象编程的补充和完善。面向对象编程是通过纵向的继承和实现来实现功能的管理。而面向切面编程是将代码中的重复的非核心的业务提取到一个公共的地方,通过动态代理技术横向插入到各个方法中去。解决非核心代码的冗余问题。
关于AOP的几个名词
横切关注点
从每一个方法中抽取出来的同一类非核心业务。业务处理的主要流程是核心关注点,其他非主要流程就是横切关注点。
通知(增强)
每一个横切关注点上要做的事情都需要一个方法来实现,这样的方法称之为通知方法
- 前置通知:在被代理的目标方法前执行
- 返回通知:在被代理的目标方法成功结束后执行
- 异常通知:在被代理的目标方法异常结束后执行
- 后置通知:在被代理的目标方法最终结束后执行
- 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置
连接点
指那些被拦截的点,在Spring中,指可以被动态代理拦截目标类的方法
切入点
定位连接点的方式,被选中的连接点
切面(aspect)
切入点和通知的结合,指的是通知类
目标(target)
被代理的目标对象
代理(proxy)
向目标对象通知之后创建的代理对象
织入(weave)
指把通知应用到目标上,生成代理对象的过程。可以再编译期间织入,也可以再运行期间织入,spring使用运行期间织入
Spring AOP框架
Spring AOP框架,是基于AOP编程思维,封装动态代理技术,简化动态代理技术实现的框架。SpringAOP内部帮助实现动态代理,只需写少量的配置,指定生效范围即可,即可完成面向切面思维编程的实现
aop只能对ioc容器内的对象创建代理对象,代理对象会存储到ioc容器
基于注解方式实现
底层技术组成
- 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口。
- cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。
- AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。
aop的实现步骤:
- 正常写核心业务和配置ioc
- 定义增强类,定义增强方法
- 配置增强类
- 开启aop的配置
aop大致实现例子:
增强类:
/***内部存储增强代码
*/
@Component
@Aspect
public class LogAdvice {@Before("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")public void start(){System.out.println("方法开始了");}@After("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")public void after(){System.out.println("方法结束了");}@AfterThrowing("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")public void error(){System.out.println("方法报错了");}
}
配置类配置aop
@Configuration
@ComponentScan("com.ergou")
//让程序允许使用AspectJ注解
@EnableAspectJAutoProxy
public class JavaConfig {
}
xml文件配置aop(和配置类二选一)
<context:component-scan base-package="com.ergou"/>
<aop:aspectj-autoproxy/>
原来的业务代码不需要做改动
测试:
@SpringJUnitConfig(value = JavaConfig.class)
public class Test {
//
@Autowiredprivate Calculator calculator;@org.junit.jupiter.api.Testpublic void test1(){int add = calculator.add(3,4);System.out.println(add);}
}
注:使用jdk的动态代理,代理类和目标对象会有同样的接口,要使用代理功能,要用目标对象的接口获取ioc容器中的对象(获取到的将会是代理对象而不是目标对象,也证明ioc中真正存储的对象是代理对象而不是目标对象)
cglib的生效场景是目标方法的类没有实现接口
五种常见的通知注解
- @Before: 前置通知
- @AfterReturning:后置通知
- @Around: 环绕通知
- @AfterThrowing:异常通知
- @After :最终通知
获取通知细节信息
需要获取方法签名、传入的实参等信息时,可以在通知方法声明JoinPoint类型的形参。
- JoinPoint 接口通过 getSignature() 方法获取目标方法的签名(方法声明时的完整信息)
- 通过目标方法签名对象获取方法名
- 通过 JoinPoint 对象获取外界调用目标方法时传入的实参列表组成的数组
@Before("execution(* com.ergou.service.impl.*.*(..))")
public void start(JoinPoint joinPoint){
//获取方法所属的目标类的信息
String simpleName = joinPoint.getTarget().getClass().getSimpleName();System.out.println(simpleName);
//获取方法名称
String name = joinPoint.getSignature().getName();System.out.println(name);
//获取访问修饰符
int modifiers = joinPoint.getSignature().getModifiers();String s = Modifier.toString(modifiers);
//获取参数列表
Object[] args = joinPoint.getArgs();for (Object o :args) {System.out.println(o);}
}
获取返回结果,要在@AfterReturning注解标注的方法内进行,先在方法的形参列表中加一个Object类型的形参变量(假设变量名取为result)用来接收返回值信息,在@AfterReturning注解中,加上属性returning,其值为接收返回值信息的形参变量名result,例:
@AfterReturning(value = "execution(* com.ergou.service.impl.*.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){System.out.println(result);
}
获取异常信息,与获取返回结果类似。要在@AfterThrowing注解标注的方法内进行,先在方法的形参列表中加一个Throwable类型的形参变量(假设变量名取为throwable)用来接收异常信息,在@AfterThrowing注解中加上属性throwing,其值为接收异常信息的形参变量名throwable,例:
@AfterThrowing(value = "execution(* com.ergou.service.impl.*.*(..))",throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint,Throwable throwable){throwable.getStackTrace();
}
切点表达式
切点表达式作用
AOP切点表达式(Pointcut Expression)是一种用于指定切点的语言,它可以通过定义匹配规则,来选择需要被切入的目标对象。
切点表达式写在通知注解的value属性中,如果没有其他属性,可以省略value=
切点表达式语法
语法细节
- ..不能作为开头,可以写做*..
- 参数列表部分,如果写做(String..),意思是参数列表只要开头的第一个参数是String类型的即可,后面随意。(..String)即为结尾为String类型的参数,前面随意。(String..int)同理。
切点表达式的提取和复用
当前类中提取:
步骤:
- 定义一个空方法,加上@Pointcut,在注解的括号中写入指定的切点表达式
- 在通知注解中的value属性中直接写@Pointcut注解标记的方法的方法名加括号即可
例:
@Aspect
@Component
public class MyAdvice {@Pointcut("execution(* com.ergou.service.impl.*.*(..))")public void pc(){}@Before("pc()")public void start(JoinPoint joinPoint){
//获取方法所属的目标类的信息
String simpleName = joinPoint.getTarget().getClass().getSimpleName();System.out.println(simpleName);
//获取方法名称
String name = joinPoint.getSignature().getName();System.out.println(name);
//获取访问修饰符
int modifiers = joinPoint.getSignature().getModifiers();String s = Modifier.toString(modifiers);
//获取参数列表
Object[] args = joinPoint.getArgs();for (Object o :args) {System.out.println(o);}}@AfterReturning(value = "pc()",returning = "result")public void afterReturning(JoinPoint joinPoint,Object result){System.out.println(result);}@After("pc()")public void after(){}@AfterThrowing(value = "pc()",throwing = "throwable")public void afterThrowing(JoinPoint joinPoint,Throwable throwable){throwable.getStackTrace();}
}
创建一个存储切点的类单独维护切点表达式:
- 创建一个类,用来存储切点表达式,里面同样用@Pointcut注解来存储切点表达式
- 通知注解中的value属性中写上要引用的切点表达式所在类的全限定符加上.方法名加括号
例:
@Component
public class MyPointcut {@Pointcut("execution(* com.ergou.service.impl.*.*(..))")public void pc(){}
}
这个存储切点表达式的类同样也要放进ioc容器
@AfterReturning(value = "com.ergou.pointcut.MyPointcut.pc()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){System.out.println(result);
}
环绕通知
环绕通知对应整个 try...catch...finally 结构,包括前面四种通知的所有功能。
环绕通知使用步骤:
- 在通知类中写一个环绕通知方法,参数列表中写一个ProceedingJoinPoint类型的参数,此参数会自动接收目标方法的信息。并在此环绕通知方法上方写上@Around注解,其value属性指定切点表达式
- 环绕通知需要在通知中,定义目标方法的执行。目标方法的执行代码为Object result = joinPoint.proceed(args)。其中,result是用来接收目标方法的返回值的对象,因为result最后还要被环绕通知方法返回;args是传入目标方法的参数,通过调用joinPoint对象的相关方法来获取。
- 用 try...catch...finally 结构将执行代码围绕起来,就可以在相应的位置放通知功能的代码
- 最后return返回接收了目标方法的返回值的result对象
例:
/***环绕通知需要在通知中,定义目标方法的执行
* @paramjoinPoint接收了目标方法的信息 比起之前的JoinPoint类型的对象,多了一个执行的功能
* @return*/
@Around("com.ergou.pointcut.MyPointcut.pc()")
public Object transaction(ProceedingJoinPoint joinPoint){Object[] args = joinPoint.getArgs();Object result = null;try {
//前置通知代码
System.out.println("开启事务");result = joinPoint.proceed(args);
//后置通知代码
System.out.println("提交事务");} catch (Throwable e) {
//异常通知代码
System.out.println("事务回滚");throw new RuntimeException(e);} finally {
//最终通知代码
System.out.println("必须执行的代码");}return result;
}
切面优先级设置
切面优先级是指要调用目标方法时,如果有多个切面,优先级高的切面的通知方法是在外层。
使用@Order注解标记在通知类上,在其中指定一个数字,值越小,优先级越高。
@Component
@Aspect
//值越小,优先级越高
@Order(2)
public class TxAdvice {@Before("com.ergou.pointcut.MyPointcut.pc()")public void begin(){}@AfterReturning("com.ergou.pointcut.MyPointcut.pc()")public void commit(){}@AfterThrowing("com.ergou.pointcut.MyPointcut.pc()")public void rollback(){}
}
优先级高的因为在外层,所以前置先执行,后置后执行
xml方式配置aop
了解即可,主要使用注解方式
<!-- 配置目标类的bean -->
<bean id="calculatorPure" class="com.atguigu.aop.imp.CalculatorPureImpl"/><!-- 配置切面类的bean -->
<bean id="logAspect" class="com.atguigu.aop.aspect.LogAspect"/><!-- 配置AOP -->
<aop:config><!-- 配置切入点表达式 --><aop:pointcut id="logPointCut" expression="execution(* *..*.*(..))"/><!-- aop:aspect标签:配置切面 --><!-- ref属性:关联切面类的bean --><aop:aspect ref="logAspect"><!-- aop:before标签:配置前置通知 --><!-- method属性:指定前置通知的方法名 --><!-- pointcut-ref属性:引用切入点表达式 --><aop:before method="printLogBeforeCore" pointcut-ref="logPointCut"/><!-- aop:after-returning标签:配置返回通知 --><!-- returning属性:指定通知方法中用来接收目标方法返回值的参数名 --><aop:after-returningmethod="printLogAfterCoreSuccess"pointcut-ref="logPointCut"returning="targetMethodReturnValue"/><!-- aop:after-throwing标签:配置异常通知 --><!-- throwing属性:指定通知方法中用来接收目标方法抛出异常的异常对象的参数名 --><aop:after-throwingmethod="printLogAfterCoreException"pointcut-ref="logPointCut"throwing="targetMethodException"/><!-- aop:after标签:配置后置通知 --><aop:after method="printLogCoreFinallyEnd" pointcut-ref="logPointCut"/><!-- aop:around标签:配置环绕通知 --><!--<aop:around method="……" pointcut-ref="logPointCut"/>--></aop:aspect>
</aop:config>
根据类型装配 bean
-
情景一
- bean 对应的类没有实现任何接口
- 根据 bean 本身的类型获取 bean
-
测试:IOC容器中同类型的 bean 只有一个
正常获取到 IOC 容器中的那个 bean 对象
-
测试:IOC 容器中同类型的 bean 有多个
会抛出 NoUniqueBeanDefinitionException 异常,表示 IOC 容器中这个类型的 bean 有多个
-
-
情景二
- bean 对应的类实现了接口,这个接口也只有这一个实现类
- 测试:根据接口类型获取 bean
- 测试:根据类获取 bean
- 结论:上面两种情况其实都能够正常获取到 bean,而且是同一个对象
- bean 对应的类实现了接口,这个接口也只有这一个实现类
-
情景三
- 声明一个接口
- 接口有多个实现类
- 接口所有实现类都放入 IOC 容器
-
测试:根据接口类型获取 bean
会抛出 NoUniqueBeanDefinitionException 异常,表示 IOC 容器中这个类型的 bean 有多个
-
测试:根据类获取bean
正常
-
-
情景四(jdk代理情景)
-
声明一个接口
-
接口有一个实现类
-
创建一个切面类,对上面接口的实现类应用通知
-
测试:根据接口类型获取bean
正常
-
测试:根据类获取bean
无法获取 原因分析:
-
-
应用了切面后,真正放在IOC容器中的是代理类的对象
-
目标类并没有被放到IOC容器中,所以根据目标类的类型从IOC容器中是找不到的
-
-
情景五(cglib代理场景)
- 声明一个类
- 创建一个切面类,对上面的类应用通知
- 测试:根据类获取 bean,能获取到
相关文章:

SSM框架,spring-aop的学习
代理模式 二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来…...

【设计模式】4、策略模式
文章目录 一、问题二、解决方案2.1 真实世界的类比2.2 策略模式结构2.3 适用场景2.4 实现方式2.5 优缺点2.6 与其他模式的关系 三、示例代码3.1 go3.2 rust 策略模式是一种行为设计模式,它能定义一系列算法,把每种算法分别放入独立的类中,以是…...

【C++学习手札】多态:掌握面向对象编程的动态绑定与继承机制(深入)
🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:世界上的另一个我 1:02━━━━━━️💟──────── 3:58 🔄 ◀️ ⏸ ▶️ ☰ &am…...

【机构vip教程】Android SDK手机测试环境搭建
Android SDK 的安装和环境变量的配置 前置条件:需已安装 jdk1.8及 以上版本 1、下载Android SDK,解压后即可(全英文路径);下载地址:http://tools.android-studio.org/index.php/sdk 2、新建一个环境变量&…...

2024.2.18
使用fgets统计给定文件的行数 #include<stdio.h> #include<string.h> int main(int argc, const char *argv[]) {FILE *fpNULL;if((fpfopen("./test.txt","w"))NULL){perror("open err");return -1;}fputc(h,fp);fputc(\n,fp);fput…...

Haproxy实验
环境: servera(Haproxy):192.168.233.132 serverb(web1):192.168.233.144 serverc(web2):192.168.233.140 serverd(客户端):192.168.233.141 servera(Haproxy): yum install haproxy -y vim /etc/haproxy/haproxy.cfg(配置文件) # 设置日志&#…...

CSRNET图像修复,DNN
CSRNET图像修复 CSRNET图像修复,只需要OPENCV的DNN...

004 - Hugo, 分类
004 - Hugo, 分类content文件夹 004 - Hugo, 分类 content文件夹 ├─.obsidian ├─categories │ ├─Python │ └─Test ├─page │ ├─about │ ├─archives │ ├─links │ └─search └─post├─chinese-test├─emoji-support├─Git教程├─Hugo分类├─…...

Vue3之ElementPlus中Table选中数据的获取与清空方法
Vue3之ElementPlus中Table选中数据的获取与清空方法 文章目录 Vue3之ElementPlus中Table选中数据的获取与清空方法1. 点击按钮获取与清空选中表格的数据1. 用到ElementPlus中Table的两个方法2. 业务场景3. 操作案例 1. 点击按钮获取与清空选中表格的数据 1. 用到ElementPlus中…...

Leetcode 516.最长回文子序列
题意理解: 给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 回文理解为元素对称的字串,这里…...

cool Node后端 中实现中间件的书写
1.需求 在node后端中,想实现一个专门鉴权的文件配置,可以这样来解释 就是 有些接口需要token调用接口,有些接口不需要使用token 调用 这期来详细说明一下 什么是中间件中间件顾名思义是指在请求和响应中间,进行请求数据的拦截处理…...

Leecode之面试题消失的数字
一.题目及剖析 https://leetcode.cn/problems/missing-number-lcci/description/ 数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗? 注意:本题相对书上原题稍作改动 示例 1&…...

STM32的三种下载方式
结果jlink,串口,stlink方式都没有问题,是当时缩减代码,看真正起作用的代码段有哪些,就把GPIO初始化中 /*开启GPIO外部时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); 把开启外部时钟的代码注释掉了。…...

华为 huawei 交换机 接口 MAC 地址学习限制接入用户数量 配置示例
目录 组网需求: 配置思路: 操作步骤: 配置文件: 组网需求: 如 图 2-14 所示,用户网络 1 和用户网络 2 通过 LSW 与 Switch 相连, Switch 连接 LSW 的接口为GE0/0/1 。用户网络 1 和用户网络 2 分别属于 VLAN10 和 V…...

使用Python生成二维码的完整指南
无边落木萧萧下,不如跟着可莉一起游~ 可莉将这篇博客收录在了:《Python》 可莉推荐的优质博主首页:Kevin ’ s blog 本文将介绍如何使用Python中的qrcode库来生成二维码。通过简单的代码示例和详细解释,读者将学习如何在Python中轻…...

排序前言冒泡排序
目录 排序应用 常见的排序算法 BubbleSort冒泡排序 整体思路 图解分析 代码实现 每趟 写法1 写法2 代码NO1 代码NO2优化 时间复杂度 排序概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递…...

红队笔记Day3-->隧道上线不出网机器
昨天讲了通过代理的形式(端口转发)实现了上线不出网的机器,那么今天就来讲一下如何通过隧道上线不出网机器 目录 1.网络拓扑 2.开始做隧道?No!!! 3.icmp隧道 4.HTTP隧道 5.SSH隧道 1.什么…...

C 练习实例70-求字符串长度
题目:写一个函数,求一个字符串的长度,在 main 函数中输入字符串,并输出其长度。 解答: #include <stdio.h> int length(char *s); int main() {int len;char str[20];printf("请输入字符串:\n");scan…...

HarmonyOS—@State装饰器:组件内状态
State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。 在状态变量相关装饰器中,State是最基础的,使变量拥有状态属性的装饰器&a…...

Linux系统——防火墙拓展及重点理解
目录 一、iptables 1.基本语法 2.四表五链——重点记忆 2.1四表 2.2五链 2.3总结 3.iptables选项示例 3.1 -Z 清空流量计数 3.2 -P 修改默认规则 3.3 -D 删除规则 3.4 -R 指定编号替换规则 5.白名单 6.通用匹配 7.示例 7.1添加回环网卡 7.2可以访问端口 7.3 主…...

阿里云短信验证码的两个坑
其它都参照官网即可,其中有两个坑需要注意: 1、除去官网pom引用的包之外,还需要引用以下包: <dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifact…...

c入门第十五篇——学而时习之(阶段性总结)
古人说:“学而时习之。”古人又说:“温故而知新。”古人还说:“读书百遍,其义自见。” 总结一个道理那就是好书要反反复复的读,学习过的知识要时常去复习它,才有可能常读常新。 我:“师弟&…...

抽象的前端
问题背景:vue3,axios 直接导致问题:路由渲染失败 问题报错:Uncaught SyntaxError: The requested module /node_modules/.vite/deps/axios.js?v7bee3286 does not provide an export named post (at LoginIn.vue:16:9) 引入组…...

UPC训练赛二十/20240217
A:无穷力量 题目描述 2022年重庆突发山火让世界看到了中国一个又一个的感人事迹:战士们第一时间奔赴火场,志愿者们自发组成团队,为救火提供一切的可能的服务,人们自发输送物资,有的志愿者甚至几天几夜没有睡觉。每个…...

【51单片机】LCD1602(江科大)
1.LCD1602介绍 LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符 显示容量:162个字符,每个字符为5*7点阵 2.引脚及应用电路 3.内部结构框图 屏幕: 字模库:类似于数码管的数…...

conda与pip的常用命令
conda的常用命令 1.查看conda版本 $ conda --version conda 23.11.02.查看conda的配置信息 $ conda infoactive environment : baseactive env location : /home/myPc/miniconda3shell level : 1user config file : /home/myPc/.condarcpopulated config files : conda vers…...

你知道什么是物联网MQTT么?
目录 你知道什么是物联网MQTT么?MQTT的基本概念MQTT的工作原理MQTT的应用场景MQTT的实例案例智能家居场景工业监控场景 你知道什么是物联网MQTT么? MQTT(Message Queuing Telemetry Transport)是一种轻量级的、基于发布/订阅模式…...

P8 pair vector
pair是一个模板类,用于表示一对值的组合,用<utility>中 pair模板有两个模板参数,t1 t2,分别表示第一个值和第二个值类型 pair类有两个成员变量,frist和 cond,分别表示第一个值与第二个值 还有一些成员函数和…...

奇异值分解(SVD)的应用——图像压缩
SVD方法是模型降阶的一类重要方法,本征正交分解(POD)和平衡截断(BT)都属于SVD类方法。 要想深入了解模型降阶技术,我们可以先从SVD的应用入手,做一个直观的了解。 1. SVD的定义和分类 我们想寻找…...

RTDETR改进系列指南
基于Ultralytics的RT-DETR改进项目.(89.9) 为了感谢各位对RTDETR项目的支持,本项目的赠品是yolov5-PAGCP通道剪枝算法.具体使用教程 自带的一些文件说明 train.py 训练模型的脚本main_profile.py 输出模型和模型每一层的参数,计算量的脚本(rtdetr-l和rtdetr-x因为thop库的问…...