[Spring] Spring AOP
🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- 1. AOP概述
- 2. Spring AOP快速入门
- 2.1 引入AOP依赖
- 2.2 编写AOP程序
- 3. Spring AOP详解
- 3.1 核心概念
- 3.1.1 切点(PointCut)
- 3.1.2 连接点(Join Point)
- 3.1.3 通知(Advice)
- 3.1.4 切面(Aspect)
- 3.2 通知类型
- 3.3 @Pointcut
- 3.4 切面优先级@Order
- 3.5 切点表达式
- 3.5.1 execution表达式
- 3.5.2 @annotation(翻译:注解)
- 4. Spring AOP原理
- 4.1 代理模式
- 4.1.1 静态代理
- 4.1.2 动态代理
1. AOP概述
- 什么是AOP?
所谓AOP,就是面相切面的编程.什么是面向切面的编程呢,切面就是指定某一类特定的问题,所以,面向切面的编程就是正对于同一类问题进行编程.
简单来说,就是针对某一类事情的集中处理. - 什么是Spring AOP?
AOP是一种思想,实现AOP的方法有很多,有Spring AOP,有AspectJ,有CGLIB等.Spring AOP是其中的一种实现方式.
某种程度上,他和我们前面提到的统一功能处理的效果差不多,但是,统一功能处理并不等同于SpringAOP,拦截器的作用维度是URL,即一次请求响应,但是AOP的作用维度更加细致,可以对包括包,类,方法,参数等进行统一功能的处理,可以实现更加复杂的业务逻辑.
举例:
现在有一些业务执行效率比较低,我们需要对接口进行优化,第一步需要定位出耗时较长的业务方法,在针对业务额来进行优化.我们就可以在每一个方法的结束和开始的地方加上时间戳,之后作差展示出来.但是在一个项目中,我们有好多接口,此时在接口中一个个低添加时间戳又不是很现实,此时我们就需要用到AOP.
接下来,我们就来看看AOP如何使用.
2. Spring AOP快速入门
需求: 统计图书管理系统各个接口和方法的执行时间.
2.1 引入AOP依赖
在pom文件中添加依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 编写AOP程序
记录Controller中每个方法的执行时间.
package com.jrj.books.component;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect//表示切面
@Component
@Slf4j
public class TimeAspect {@Around("execution(* com.jrj.books.controller.*.*(..)))")//切点表达式public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//参数是连接点long begin = System.currentTimeMillis();Object ret = proceedingJoinPoint.proceed();//执行连接点方法long end = System.currentTimeMillis();log.info("执行耗时:" + (end-begin));return ret;}
}
上述代码的测试结果如下:
我们看到了接口返回了执行耗时.
下面我们对上面的代码进行详细的解释:
-
@Aspect
: 表示的是切面.表示这个类是一个切面类. -
@Around
: 环绕通知,在目标方法前后都会有代码执行.
-
proceedingJoinPoint.proceed()
: 执行加入切面的连接点. -
execution(* com.jrj.books.controller.*.*(..)))
:切点表达式,表示这个切面对该项目下的那个方法生效. -
ProceedingJoinPoint proceedingJoinPoint
: 加入切面的连接点.
3. Spring AOP详解
3.1 核心概念
3.1.1 切点(PointCut)
切点,也就是切入点,我们一般在通知类型注解的后面使用execution切点表达式来描述切点.切点就是告诉程序对那些方法的功能进行加强.
3.1.2 连接点(Join Point)
满足切点表达式规则的方法,就是连接点,也就是可以被该切面所作用的方法.连接点的数据类型是:ProceedingJoinPoint
.也就是com.jrj.books.controller.
包下的所有方法都是该切面的连接点.
切点与连接点之间的关系
连接点事满足切点表达式的元素,切点可以看做是一个保存了众多连接点的集合.
3.1.3 通知(Advice)
通知就是具体要做的工作,指在指定的方法中重复那些逻辑,也就是共性功能.
比如上面实现计算运行前后的时间差的业务逻辑就叫做通知.
3.1.4 切面(Aspect)
切面=切点+通知
切面既包含了逻辑的定义,也包含连接点的定义.
切面说在的类,我们一般称为切面类.
3.2 通知类型
SpringAOP中的通知类型有以下几种:
- @Around:环绕通知,表示该方法在目标方法前后都会被执行.
- @Before:前置通知,表示该方法只在目标方法之前被执行.
- @After:后置通知,表示该方法只在目标方法之后被执行,无论是否有异常发生.
- @AfterReturning:返回后通知,表示该方法只在目标方法之后被执行,方法返回了之后才会执行,有异常发生不会执行.
- @AfterThrowing:异常后通知.表示方法在发生异常之后执行.
代码演示:
@Slf4j
@Component
@Aspect
public class AspectDemo {@Before("execution(* com.jrj.aspect.*.*(..))")public void before(){log.info("before method");}@After("execution(* com.jrj.aspect.*.*(..))")public void after(){log.info("after method");}@Around("execution(* com.jrj.aspect.*.*(..))")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {log.info("around before");Object o = proceedingJoinPoint.proceed();log.info("around after");return o;}@AfterReturning("execution(* com.jrj.aspect.*.*(..))")public void afterReturning(){log.info("afterReturning method");}@AfterThrowing("execution(* com.jrj.aspect.*.*(..))")public void afterThrowing(){log.info("afterThrowing method");}
}
[注意] 被@Around
标志的方法必须有形式和返回值.其他注解标志的方法可以加上JoinPoint
类型的形式参数,**用来获取原始方法的数据.
下面我们准备测试代码:其中一个正常执行,另外一个制造一些异常出来.
@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping("/t1")public String t1(){return "t1";}@RequestMapping("t2")public String t2(){int a = 10/0;return "";}
}
下面我们来执行代码,观察后端日志:
- 正常执行的代码
在程序运行正常的情况下,@AfterThrowing
注解的方法不会执行.
从上面的执行结果中,我们可以看出:@Around
有前置逻辑和后置逻辑两部分,这两个逻辑再内层就是@Before
和@After
标识的方法,再往内层就是@AfterReturning
标注的方法.
- 异常的情况
在异常发生的情况下,@Around
标识的后置逻辑不会被执行.@AfterReturning
标识的方法不会被执行.@AfterThrowing
表示的方法会被执行.
[注意事项]
• @Around 环绕通知需要调用ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行.
• @Around 环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的.
3.3 @Pointcut
上面的代码中存在一个问题,就是切点表达式大量重复,这时候,Spring提供了@Pointcut
注解用来把公共的切点表达式提取出来.到时候直接使用方法名把它提取出来即可.
@Slf4j
@Component
@Aspect
public class AspectDemo {@Pointcut("execution(* com.jrj.aspect.*.*(..))")public void pointCut(){}@Before("pointCut()")public void before(){log.info("before method");}@After("pointCut()")public void after(){log.info("after method");}@Around("pointCut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {log.info("around before");Object o = proceedingJoinPoint.proceed();log.info("around after");return o;}@AfterReturning("pointCut()")public void afterReturning(){log.info("afterReturning method");}@AfterThrowing("pointCut()")public void afterThrowing(){log.info("afterThrowing method");}
}
[注意] 当切点使用private修饰的时候,仅可以在该类中使用.当其他切面类也需要使用到该切点的时候,就需要把private改成public,并使用全限定方法名.
3.4 切面优先级@Order
当我们在一个项目中,定义了多个切面类,并且这些切面类的多个切点都匹配到了同一个目标方法,当目标方法运行的时候,这些切面类中的方法都会执行,那么这些方法执行的顺序是什么样的呢?我们通过代码来验证.
@RequestMapping("/test2")
public class TestController {@RequestMapping("/t1")public String t1(){return "t1";}
}
@Slf4j
@Component
@Aspect
public class AspectDemo1 {@Pointcut("execution(* com.jrj.aspect.order.*.*(..))")public void pt(){}@Before("pt()")public void before(){log.info("AspectDemo1 before");}@After("pt()")public void after(){log.info("AspectDemo1 after");}
}
@Slf4j
@Component
@Aspect
public class AspectDemo2 {@Before("com.jrj.aspect.order.AspectDemo1.pt()")public void before(){log.info("AspectDemo2 before");}@After("com.jrj.aspect.order.AspectDemo1.pt()")public void after(){log.info("AspectDemo2 after");}
}
@Slf4j
@Component
@Aspect
public class AspectDemo3 {@Before("com.jrj.aspect.order.AspectDemo1.pt()")public void before(){log.info("AspectDemo3 before");}@After("com.jrj.aspect.order.AspectDemo1.pt()")public void after(){log.info("AspectDemo3 after");}
}
观察日志:
通过上述程序的运行结果,可以看出:
存在多个切面类对应一个方法的时候,默认按照切面类的字母序排序.Before和After成对称式分布.
- @Before:字母序靠前的先通知.
- @After:字母序靠后的先通知.
此外,我们还可以通过Spring的注解,来控制切面的执行顺序:@Order
.
使用方式如下:
@Slf4j
@Component
@Aspect
@Order(1)
public class AspectDemo1 {...
}
@Slf4j
@Component
@Aspect
@Order(3)
public class AspectDemo2 {...
}
@Slf4j
@Component
@Aspect
@Order(2)
public class AspectDemo3 {...
}
观察日志:
我们可以得出以下结论:
- @Before:数字小的先执行
- @After:数字大的先执行
和上面一样,Before和After也是呈对称式分布.
3.5 切点表达式
上面的代码中,我们一直在使用切点表达式来描述切点,下面我们来介绍一下切点表达式.
切点表达式最常见的有以下两种方式:
execution(...)
: 根据方法的签名来匹配.@annotation
:根据注解匹配.
3.5.1 execution表达式
execution(<访问限定符> <返回类型> <包名.类名.方法(方法参数)><异常>)
其中访问限定符可以省略.
切点表达式支持通配符:
*
:匹配任何字符,只可以匹配一个元素,可以匹配返回类型,包名,类名,方法名,方法参数.
a. 包名使用*
表示任意包(⼀层包使用⼀个*
)
b. 类名使用*
表示任意类
c. 返回值使用*
表示任意返回值类型
d. 方法名使用*
表示任意方法
e. 参数使用*
表示⼀个任意类型的参数..
:匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数.
a. 使用..
配置包名,标识此包以及此包下的所有子包
b. 可以使用..
配置参数,任意个任意类型的参数
[注意]*
只可以匹配一个元素,..
可以匹配多个元素.
举例:
- 匹配TestController下的所有无参方法.
execution(* com.example.demo.controller.TestController.*())
- 匹配TestController下的所有方法
execution(* com.example.demo.controller.TestController.*(..))
- 匹配com.example.demo包下,子孙包下的所有类的所有方法
execution(* com.example.demo..*(..))
- 匹配所有包下面的TestController类的所有方法.
execution(* com..TestController.*(..))
3.5.2 @annotation(翻译:注解)
准备接口测试代码:
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/u1")public String user1(){return "u1";}@RequestMapping("/u2")public String user2(){return "u2";}
}
@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping("/t1")public String t1(){return "t1";}@RequestMapping("t2")public String t2(){int a = 10/0;return "";}
}
如果我们要匹配多个无规则的方法呢?比如UserController中的user1方法和TestController中的t1方法.这时候使用execution显得有些麻烦.这时候我们就可以借助切点表达式的另一种方式.@annotation
注解来描述一类切点.具体实现步骤如下:
- 编写自定义注解
- 使用
@annotation
表达式来描述切点. - 在连接点的方法上添加自定义注解.
- 自定义注解
创建一个自定义注解类,创建的方式和创建类是一样的.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {}
@Target
注解,代表的是注解可以修饰的对象,常用的有以下四种取值:
ElementType.TYPE
:用于描述类、接口(包括注解类型)或enum声明
ElementType.METHOD
:描述方法
ElementType.PARAMETER
:描述参数
ElementType.TYPE_USE
:可以标注任意类型@Retention
注解,代表的是Annotation
被保留的时间.表示的是该注解的生命周期.
RetentionPolicy.SOURCE
:表示注解仅存在于源代码中,编译成字节码后会被丢弃.
RetentionPolicy.CLASS
:编译时注解.表示注解存在于源代码和字节码中,但在运行时会被丢弃.
RetentionPolicy.RUNTIME
:运行时注解,表示注解存在于源代码,字节码和运行时中.
- 切面类
使用@annotation
切点表达式定义切点,只对@MyAspect生效.
@Aspect
@Slf4j
@Component
public class AspectDemo4 {@Before("@annotation(com.jrj.aspect.MyAspect)")public void before(){log.info("before method");}@After("@annotation(com.jrj.aspect.MyAspect))")public void after(){log.info("after method");}
}
- 为指定方法添加自定义注解,在加上自定义注解之后,在调用接口的时候就会执行切面中的方法.
在测试代码的方法下面加上自定义注解:
@RestController
@RequestMapping("/test")
public class TestController {@MyAspect@RequestMapping("/t1")public String t1(){...}@MyAspect@RequestMapping("t2")public String t2(){...}
}
@RestController
@RequestMapping("/user")
public class UserController {@MyAspect@RequestMapping("/u1")public String user1(){...}@MyAspect@RequestMapping("/u2")public String user2(){...}
}
观察日志:
我们发现测试的接口打印出了制定的日志.
4. Spring AOP原理
SpringAOP使用的是动态代理模式来实现的.他所涉及的设计模式是代理模式.
4.1 代理模式
代理模式,也叫委托模式
为其他对象提供⼀种代理以控制对这个对象的访问.它的作用就是通过提供⼀个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用.
举例说明代理模式:
房屋中介与房东,艺人与经纪人,老板与秘书.
代理模式的主要角色:
- Subject:业务接口类.这里定义的是代理和被代理对象要做的事情.可以抽象类或者接口.
- RealSubject:业务实现类.具体的业务执行,也就是被代理的对象.
- Proxy:代理类.RealSubject的代理.
根据代理创建的时期,可以把代理模式分为动态代理和静态代理.
4.1.1 静态代理
静态代理:在程序运行前,代理类的.class文件就已经存在了.(在出租房子之前,中介已经做好了相关的工作就等租户来租房子了)
- 接口定义(定义房东和中介要做的事情)
public interface HouseSubject {void rentHouse();
}
- 实现接口(房东出租房子)
public class RealHouseProxy implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我要出租房子");}
}
- 代理(中介,帮房东出租房子)
public class Proxy implements HouseSubject{private RealHouseProxy realHouseProxy;public Proxy(RealHouseProxy realHouseProxy) {this.realHouseProxy = realHouseProxy;}@Overridepublic void rentHouse() {System.out.println("我要代理出租房子");realHouseProxy.rentHouse();System.out.println("代理完成");}
}
- 使用
public static void main(String[] args) {RealHouseProxy realHouseProxy1 = new RealHouseProxy();Proxy proxy = new Proxy(realHouseProxy1);proxy.rentHouse();
}
运行结果:
虽然静态代理也完成了代理,但是由于代码是写死的,对目标方法的增强都是手动来完成的,非常不灵活,所以我们有了动态代理.
4.1.2 动态代理
我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个代理对象的工作推迟到程序运行的时候由JVM来实现,也就是在程序运行的时候,根据需要动态创建代理.
常见的动态代理实现模式有两种方式:
- jdk动态代理
实现步骤如下:- 首先定义一个接口及其实现类(相当于房东).
- 之后自定义一个类,实现
InvocationHandler
接口并重写invoke
方法,在invoke方法中调用目标方法(被代理的方法)并自定义一些处理逻辑. - 这里调用方法的时候使用的是反射的方式,使用invoke()方法来调用方法(反射方式),并传入被代理对象和args(数组中存放的是方法的参数)参数.
- 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h
方法创建代理对象.
public interface HouseSubject {void rentHouse();
}
public class RealHouseProxy implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我要出租房子");}
}
public class JDKInvocationHandler implements InvocationHandler {private Object object;public JDKInvocationHandler(Object o) {this.object = o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是代理,开始代理");Object object = method.invoke(this.object,args);System.out.println("代理结束");return object;}public static void main(String[] args) {HouseSubject houseSubject = new RealHouseProxy();HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(houseSubject.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(houseSubject));proxy.rentHouse();}
}
[注意事项]
1. 在动态代理类中,由于代理的对象可以是任何类型的,所以被代理对象应该是Object类型的.
2. 在使用newInstance方法创建代理对象的时候,第一个传入的是被代理对象的加载器,第二个传入的是一个class类的数组,数组中放着被代理对象接口的class,最后一个放的是我们提前设定好的代理类的对象.
- CGLIB动态代理
jdk代理致命的问题就是他只可以代理接口(实现一个接口的类),但是CGLIB动态代理可以既可以实现接口,又可以实现类.
CGLIB动态代理类实现步骤:- 引入CGLIB依赖
- 定义一个被代理类
- 自定义一个代理类,并实现
MethodInterceptor
接口,实现intercept
方法,Intercept
方法用于调用被代理类的方法,方法和jdk代理中的invoke
类似. - 通过
Enhancer
类的create()
方法创建代理对象.在其中传入接口的class和之前写好的自定义CGLIB代理类的对象.
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
public interface HouseSubject {void rentHouse();
}
public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我要出租房子");}
}
public class CGLIBInterceptor implements MethodInterceptor {private Object object;public CGLIBInterceptor(Object object) {this.object = object;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("我是代理,开始代理");Object o1 = methodProxy.invoke(object,objects);System.out.println("代理结束");return o1;}
}
public class Main {public static void main(String[] args) {HouseSubject houseSubject = new RealHouseSubject();HouseSubject proxy = (HouseSubject) Enhancer.create(HouseSubject.class,new CGLIBInterceptor(houseSubject));proxy.rentHouse();}
}
CGLB代理模式和jdk代理模式非常类似.
相关文章:

[Spring] Spring AOP
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...

鸿蒙 webview 实现顶部 Progress进度条
1, 先看效果 2, 直接cv代码 import web_webview from ohos.web.webview;interface PerUrl {url: string,age: number } Component export struct webviews {controller: web_webview.WebviewController new web_webview.WebviewController();ports: web_webview.WebMessageP…...
Pytest-BDD实现接口自动化测试,并附全部代码
引言 在之前的文章中简单的介绍了怎么使用Pytest-BDD进行接口测试,可以参考《pytest-bdd 行为驱动自动化测试》。本篇文章主要介绍使用Pytest-BDD实现接口自动化测试。后面的文章会介绍生成测试报告,和流程性接口测试。 feature文件 首先我们先整理好…...

Sqli-labs-master靶场--布尔盲注
目录 1、布尔盲注 2、布尔盲注的流程(以靶场less-8为例) 2.1输入id尝试是否存在注入点 2.1.1通过以上尝试,联想到可能是布尔盲注 2.2猜测数据库长度 2.3获取数据库名 2.3.1python脚本获取 代码: 获取结果为: …...
【QGroundControl二次开发】十. QT添加GStreamer视频播放同时保存
上一章介绍使用QT播放GStreamer视频流 【QGroundControl二次开发】八. QT实现播放gstreamer视频。 这章介绍如何在原有基础上保存为视频,同时保存为一个个规定大小的小视频。 先展示代码: #include <QApplication> #include <QWidget> #include <QtConcurrent…...

double类型 精度丢失的问题
前言 精度丢失的问题是在其他计算机语言中也都会出现,float和double类型的数据在执行二进制浮点运算的时候,并没有提供完全精确的结果。产生误差不在于数的大小,而是因为数的精度。 一、double进行运算时,经常出现精度丢失 0.10.2使用计算…...

C++ 重要特性探究
shared_from_this 使用分析 场景 类的成员函数需要获取指向自身的shared_ptr的时候类成员函数传递shared_ptr给其他函数或者对象的时候,目的是为了管理对象生命周期使用方法 首先类必须继承 std::enable_shared_from_this<T>必须使用 shared_from_this 获取指…...
c++_游戏_狼人杀
思路主要包括以下几个部分: 角色分配:代码中通过随机数的方式给狼人、平民、预言家和法师等角色进行分配,保证每个角色的数量和身份的随机性。 游戏进行:根据狼人、平民、预言家和法师等角色的身份,游戏进行了夜晚和白…...

MySQL——数据类型、索引的建立、数据的约束
文章目录 数据类型索引的建立普通索引唯一索引使用ALTER 命令添加和删除索引使用ALTER 命令添加和删除主键显示索引信息 数据的约束非空约束:not null,值不能为null唯一约束:unique,值不能重复主键约束:primary key外键…...

常见框架漏洞详解③!!
Apache Apache 是世界使⽤排名第⼀的 Web 服务器软件。它可以运⾏在⼏乎所有⼴泛使⽤的计算 机平台上,由于其跨平台和安全性被⼴泛使⽤,是最流⾏的 Web 服务器端软件之⼀。 apache⽬录结构: bin:存放常⽤命令⼯具,如h…...
大数据基础知识
大数据(Big Data)是指无法用传统数据处理工具和技术有效处理的大规模、复杂的数据集。大数据技术通过对这些数据进行存储、处理和分析,从中提取有价值的信息和见解。 1. 大数据的特点 大数据通常具有以下四个主要特点,被称为“4…...

SQL Server 的透明数据加密
透明数据加密是SQL Server数据库安全众多特性中的一个,本文只针对透明数据加密。 在此测试之前,已经按照文档如何快速获得一个测试用SQL Server企业版创建了一个SQL Server 2019,并按照文档为SQL Server安装示例数据库AdventureWorks安装了…...
Windows图形界面(GUI)-MFC-C/C++ - 列表视图(List Control) - CListCtrl
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 列表视图(List Control) - CListCtrl 创建列表视图 设置列表视图属性 成员函数 注意事项 示例代码 列表视图(List Control) - CListCtrl 创建列表视图 在对话框编辑器中ÿ…...

一机两用的简单介绍
电子政务外网终端使用过程的风险与挑战 1、终端防护弱,失陷风险大 政务外网终端具备访问互联网能力,造成政务外网终端极易感染僵木蠕病毒,破坏正常办公 政务外网终端易被攻击失陷,成为从互联网攻击政务外网的跳板机 2、VPN漏洞…...

uniapp离线打包热更新失败-AndroidStudio离线打包apk后无法下载打开-热更新失败-plus.runtime.install失败
效果图 仅安卓 前言 1.plus.runtime.install一直fail(20240808), uni.openDocument可以打开本地apk文件 2.权限问题需小心 跑通前提 1.先确定apk地址有效,浏览器中手动下载可安装 2.确保已添加离线打包AndroidStudio的“android.permission.INSTALL_PACKAGES”权…...

深植根基、蓬勃向上 | openKylin 2.0正式发布!
2024年8月8日,openKylin 2.0版本正式发布!该版本默认搭载Linux 6.6 LTS内核,完成180操作系统核心组件自主选型升级,深度融合AI技术,上线麒麟AI助手等实用AI功能,并为用户带来包括开明软件包格式、不可变系统…...

【Material-UI】按钮组:尺寸与颜色详解
文章目录 一、按钮组概述1. 组件介绍2. 基本用法 二、按钮组的尺寸(Sizes)1. 小尺寸(Small)2. 中等尺寸(Medium)3. 大尺寸(Large) 三、按钮组的颜色(Colors)1…...

app抓包 burp配置
证书导出 模拟器安装证书 点击安装证书 将证书直接拖进来就行 配置代理 打开浏览器抓包...

图像与像素:利用ImageJ分析荧光显微镜图像|QuPath基础教程1|24-08-08
内容概要 数字图像由像素组成,每个像素具有一个数值,通常与检测到的光线相关。相同的像素值可以通过不同的方式进行显示。在科学图像处理中,可以通过修改查找表来独立于像素值改变图像外观。 一、引言 图像由其最小的组成单位——像素构成。…...

Prompt Fuzzer:用于增强 GenAI 应用程序的开源工具
Prompt Fuzzer 是一个开源工具,可以评估GenAI应用程序的系统提示针对基于动态 LLM 的威胁的安全性。 Prompt Fuzzer 功能: 1. 模拟十几种类型的 GenAI 攻击。 2. 该工具会根据系统提示自动进行情境化,针对与 GenAI 应用程序相关的特定主题或行…...

通过阿里云 DashScope API 调用通义千问
获取API Key 百炼控制台https://bailian.console.aliyun.com/?tabmodel#/api-key 步骤 1:安装 DashScope SDK pip install dashscope 步骤 2:LangChain 调用 from langchain_community.llms import Tongyi# 设置阿里云 API Key(从环境变…...

力扣面试150题--除法求值
Day 62 题目描述 做法 此题本质是一个图论问题,对于两个字母相除是否存在值,其实就是判断,从一个字母能否通过其他字母到达,做法如下: 遍历所有等式,为每个变量分配唯一的整数索引。初始化一个二维数组 …...

【学习笔记】Python金融基础
Python金融入门 1. 加载数据与可视化1.1. 加载数据1.2. 折线图1.3. 重采样1.4. K线图 / 蜡烛图1.5. 挑战1 2. 计算2.1. 收益 / 回报2.2. 绘制收益图2.3. 累积收益2.4. 波动率2.5. 挑战2 3. 滚动窗口3.1. 创建移动平均线3.2. 绘制移动平均线3.3 Challenge 4. 技术分析4.1. OBV4.…...

Paraformer分角色语音识别-中文-通用 FunASR demo测试与训练
文章目录 0 资料1 Paraformer分角色语音识别-中文-通用1 模型下载2 音频识别测试3 FunASR安装 (训练用)4 训练 0 资料 https://github.com/modelscope/FunASR/blob/main/README_zh.md https://github.com/modelscope/FunASR/blob/main/model_zoo/readm…...
kubeadm安装k8s
1、环境准备 1.1、升级系统内核 参考另一篇文章:https://blog.csdn.net/u012533920/article/details/148457715?spm1011.2415.3001.5331 1.2、设置Hostname cat <<EOF > /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhos…...

零基础玩转物联网-串口转以太网模块如何快速实现与TCP服务器通信
目录 1 前言 2 环境搭建 2.1 硬件准备 2.2 软件准备 2.3 驱动检查 3 TCP服务器通信配置与交互 3.1 硬件连接 3.2 开启TCP服务器 3.3 打开配置工具读取基本信息 3.4 填写连接参数进行连接 3.5 通信测试 4 总结 1 前言 TCP是TCP/IP体系中的传输层协议,全称为Transmiss…...

2025软件供应链安全最佳实践︱证券DevSecOps下供应链与开源治理实践
项目背景:近年来,云计算、AI人工智能、大数据等信息技术的不断发展、各行各业的信息电子化的步伐不断加快、信息化的水平不断提高,网络安全的风险不断累积,金融证券行业面临着越来越多的威胁挑战。特别是近年以来,开源…...

Docker镜像无法拉取问题解决办法
最近再学习RabbitMQ,需要从Docker镜像中拉取rabbitMQ,但是下拉失败 总的来说就是无法和docker镜像远程仓库建立连接 我又去尝试ping docker.io发现根本没有反应,还是无法连接找了许多办法还是没有办法解决,最后才发现是镜像问题&a…...

每日Prompt:双重曝光
提示词 新中式,这幅图像将人体头像轮廓与山水中式建筑融为一体,双重曝光,体现了反思、内心平静以及人与自然相互联系的主题,靛蓝,水墨画,晕染,极简...

html 滚动条滚动过快会留下边框线
滚动条滚动过快时,会留下边框线 但其实大部分时候是这样的,没有多出边框线的 滚动条滚动过快时留下边框线的问题通常与滚动条样式和滚动行为有关。这种问题可能出现在使用了自定义滚动条样式的情况下。 注意:使用方法 6 好使,其它…...