详解Spring AOP(二)
目录
1.切点表达式
1.1execution表达式
1.2 @annotation
1.2.1自定义注解@MyAspect
1.2.3添加自定义注解
2.Sping AOP原理
2.1代理模式
2.1.1静态代理
2.1.2动态代理
2.1.3JDK动态代理
2.1.4CGLIB动态代理
3.总结
承接上文:详解Spring AOP(一)
1.切点表达式
之前的代码中,我们一直在使用切点表达式来描述切点.下面我们来介绍一下切点表达式的语法.切点表达式常见有两种表达方式:
execution(.....):根据方法的签名来匹配
@annotation(...):根据注解匹配
1.1execution表达式
execution() 是最常用的切点表达式, 用来匹配⽅法, 语法为:
execution(<访问修饰符> <返回类型> <包名 .类名 .方法(方法参数)> <异常>)
其中访问修饰符和异常可以省略
切点表达式支持通配符表达:
*:匹配任意字符,只匹配一个元素(返回类型,包,类名,方法或者方法参数)
包名使用*表示任意包(一层包使用一个*)
类名使用*表示任意类
返回值使用*表示任意返回值类型
方法名使用*表示任意方法
参数使用*表示一个任意类型的参数
.. :匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数
使用..配置包名,标识此包以及此包下的所有子包
可以使用..配置参数,任意个任意类型的参数
切点表达式示例:
TestController 下的 public修饰, 返回类型为String 方法名为t1, 无参方法
execution(public String com.example.demo.controller.TestController.t1())
省略访问修饰符
execution(String com.example.demo.controller.TestController.t1())
匹配所有返回类型
execution(* com.example.demo.controller.TestController.t1())
匹配TestController 下的所有无参方法
execution(* com.example.demo.controller.TestController.*())
匹配controller包下所有的类的所有方法
execution(* com.example.demo.controller.*.*(..))
匹配所有包下面的TestController
execution(* com..TestController.*(..))
匹配com.example.demo包下, 子孙包下的所有类的所有方法
execution(* com.example.demo..*(..))
1.2 @annotation
execution表达式更适用有规则的,如果我们要匹配多个无规则的方法,比如:TestController中的t1()和UserController中的u1()这两个方法.
这个时候我们使用execution这种切点表达式来描述就不是很方便了.
我们可以借助自定义注解的方式以及切点表达式@annotation来描述这一类的切点实现步骤:
1.编写自定义注解
2.使用@annotation表达式来描述切点
3.在连接点的方法上添加自定义注解
准备测试代码:
@RequestMapping("/test") @RestControllerpublic class TestController { @RequestMapping("/t1")public String t1() {return "t1";} @RequestMapping("/t2")public boolean t2() {return true;}
}
@RequestMapping("/user")@RestControllerpublic class UserController {@RequestMapping("/u1")public String u1(){return "u1";}@RequestMapping("/u2")public String u2(){return "u2";}}
1.2.1自定义注解@MyAspect
创建⼀个注解类(和创建Class文件⼀样的流程, 选择Annotation就可以了)
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyAspect {}
@Target标识了Annotation所修饰的对象范围,即该注解可以用在什么地方.
常用取值:
ElementType.TYPE:用于描述类、接口(包括注解类型)或enum声明
ElementType.METHOD:描述方法
ElementType.PARAMETER:描述参数
ElementType.TYPE_USE:可以标注任意类型
@Retention指Annotation被保留的时间长短,标明注解生命周期@Retention的取值有三种:
1.RetentionPolicy.SOURCE:表示注解仅存在于源代码中,编译成字节码后会被丢弃.这意味着在运行时无法获取到该注解的信息,只能在编译时使用.比如@SuppressWarnings,以及lombok提供的注解@Data,@slf4j
2.RetentionPolicy.CLASS:编译时注解.表示注解存在于源代码和字节码中,但在运行时会被丢弃.这意味着在编译时和字节码中可以通过反射获取到该注解的信息,但在实际运行时无法获取.通常用于一些框架和工具的注解.
3.RetentionPolicy.RUNTIME:运行时注解.表示注解存在于源代码,字节码和运行时中.这意味着在编译时,字节码中和实际运行时都可以通过反射获取到该注解的信息.通常用于一些需要在运行时处理的注解,如Spring的@Controller @ResponseBody
1.2.2切面类
使用@annotation切点表达式定义切点,只对@MyAspect生效
切面类代码如下:
@Slf4j@Component@Aspectpublic class MyAspectDemo { //前置通知@Before("@annotation(com.example.demo.aspect.MyAspect)")public void before(){log.info("MyAspect -> before ...");}//后置通知@After("@annotation(com.example.demo.aspect.MyAspect)")public void after(){log.info("MyAspect -> after ...");}}
1.2.3添加自定义注解
在TestController中的t1()和UserController中的u1()这两个方法上添加自定义注解@MyAspect,其他方法不添加
@MyAspect@RequestMapping("/t1")public String t1() {return "t1";}@MyAspect@RequestMapping("/u1")public String u1(){return "u1";}
运行程序测试接口:
http://127.0.0.1:8080/test/t1
观察日志:
可以看到,切面通知被执行了
继续测试:
http://127.0.0.1:8080/test/t2, 切面通知未执行
http://127.0.0.1:8080/user/u1 , 切面通知执行
2.Sping AOP原理
上面我们主要学习了Spring AOP的应用,接下来我们来学习SpringAOP的原理,也就是Spring是如何实现AOP的.
Spring AOP是基于动态代理来实现AOP的,学习内容主要分以下两部分
1.代理模式
2.Spring AOP源码剖析
2.1代理模式
代理模式,也叫委托模式.
定义:为其他对象提供一种代理以控制对这个对象的访问.它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用.
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用.
使用代理前:

房屋中介:房屋进行租赁时,卖方会把房屋授权给中介,由中介来代理看房,房屋咨询等服务.
秘书/助理:合作伙伴找老板谈合作,需要先经过秘书/助理预约.
代理模式的主要角色:
1. Subject: 业务接口类. 可以是抽象类或者接口(不⼀定有)
2. RealSubject: 业务实现类. 具体的业务执行 , 也就是被代理对象.
3. Proxy: 代理类. RealSubject的代理.
比如房屋租赁
Subject 就是提前定义了房东做的事情, 交给中介代理, 也是中介要做的事情
RealSubject: 房东
Proxy: 中介
UML类图如下:
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.
根据代理的创建时期,代理模式分为静态代理和动态代理.
静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了.
动态代理:在程序运行时,运用反射机制动态创建而成.
2.1.1静态代理
静态代理:在程序运行前,代理类的.class文件就已经存在了.(在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了)
我们通过代码来加深理解.以房租租赁为例
1.定义接口(定义房东要做的事情,也是中介需要做的事情)
public interface HouseSubject {void rentHouse();
}
2.实现接口(房东出租房子)
public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东 , 我出租房⼦");}}
3.实现代理(中介,帮房东出租房子)
public class HouseProxy implements HouseSubject{ //将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;} @Overridepublic void rentHouse() { //开始代理System.out.println("我是中介 , 开始代理"); //代理房东出租房⼦houseSubject.rentHouse(); //代理结束System.out.println("我是中介 , 代理结束"); }}
4.编写测试用例:
public class StaticMain {public static void main(String[] args) {HouseSubject subject = new RealHouseSubject(); //创建代理类HouseProxy proxy = new HouseProxy(subject); //通过代理类访问⽬标⽅法proxy.rentHouse(); }}
运行结果:
上面这个代理实现方式就是静态代理
从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活.所以日常开发几乎看不到静态代理的场景.
接下来新增需求:中介又新增了其他业务:代理房屋出售我们需要对上述代码进行修改
1.接口定义修改
public interface HouseSubject {void rentHouse();void saleHouse();}
2.接口实现修改
public class RealHouseSubject implements HouseSubject{ @Overridepublic void rentHouse() {System.out.println("我是房东 , 我出租房⼦");} @Overridepublic void saleHouse() {System.out.println("我是房东 , 我出售房⼦");}}
3.代理类修改
public class HouseProxy implements HouseSubject{ //将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;} @Overridepublic void rentHouse() { //开始代理System.out.println("我是中介 , 开始代理"); //代理房东出租房⼦houseSubject.rentHouse(); //代理结束System.out.println("我是中介 , 代理结束"); }@Overridepublic void saleHouse() { //开始代理System.out.println("我是中介 , 开始代理"); //代理房东出租房⼦houseSubject.saleHouse(); //代理结束System.out.println("我是中介 , 代理结束"); }}
从上述代码可以看出,我们修改接口(Subject)和业务实现类(RealSubject)时,还需要修改代理类(Proxy).
同样的,如果有新增接口(Subject)和业务实现类(RealSubject),也需要对每一个业务实现类新增代理类(Proxy).
既然代理的流程是一样的,有没有一种办法,让他们通过一个代理类来实现呢?这就需要用到动态代理技术了
2.1.2动态代理
相比于静态代理来说,动态代理更加灵活.
我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现.也就是说动态代理在程序运行时,根据需要动态创建生成.比如房屋中介,我不需要提前预测都有哪些业务,而是业务来了我再根据情况创建.我们先看代码再来理解.
Java也对动态代理进行了实现,并给我们提供了一些API,常见的实现方式有两种:
2.1.3JDK动态代理
JDK动态代理类实现步骤
1.定义一个接口及其实现类(静态代理中的HouseSubject和RealHouseSubject)
2.自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑
3.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[ ] interfaces,InvocationHandlerh)方法创建代理对象
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method; public class JDKInvocationHandler implements InvocationHandler { //⽬标对象即就是被代理对象private Object target; public JDKInvocationHandler(Object target) {this.target = target;} @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 代理增强内容System.out.println("我是中介 , 开始代理"); //通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args); //代理增强内容System.out.println("我是中介 , 代理结束");return retVal; }}
创建⼀个代理对象并使用
public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();//创建⼀个代理类:通过被代理类、被代理实现的接口、⽅法调用处理器来创建HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}}
1.InvocationHandler
InvocationHandler接口是Java动态代理的关键接口之一,它定义了一个单一方法invoke(),用于处理被代理对象的方法调用.
public interface InvocationHandler {/*** 参数说明* proxy:代理对象* method:代理对象需要实现的⽅法,即其中需要重写的⽅法* args:method所对应⽅法的参数*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}
通过实现InvocationHandler接口,可以对被代理对象的方法进行功能增强.
2.Proxy
Proxy类中使用频率最高的方法是:newProxyInstance(),主要用来生成一个代理对象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{//...代码省略}
这个方法一共有3个参数:
Loader:类加载器,用于加载代理对象.
interfaces:被代理类实现的一些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的一些类)
h:实现了InvocationHandler接口的对象
2.1.4CGLIB动态代理
JDK动态代理有一个最致命的问题是其只能代理实现了接口的类.
有些场景下,我们的业务代码是直接实现的,并没有接口定义.为了解决这个问题,我们可以用CGLIB动态代理机制来解决.
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成.CGLIB通过继承方式实现代理,很多知名的开源框架都使用到了CGLIB.例如Spring中的AOP模块中:如果目标对象实现了接口,则默认采用JDK动态代理,否则采用CGLIB动态代理
CGLIB动态代理类实现步骤
1.定义一个类(被代理类)
2.自定义MethodInterceptor并重写intercept方法,intercept用于增强目标方法,和JDK动态代理中的invoke方法类似
3.通过Enhancer类的create()创建代理类
添加依赖
和JDK 动态代理不同, CGLIB(Code Generation Library) 实际是属于⼀个开源项⽬ ,如果你要使用它的话 ,需要手动添加相关依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version> </dependency>
⾃定义 MethodInterceptor(方法拦截器) 实现MethodInterceptor接口
public class CGLIBInterceptor implements MethodInterceptor {//⽬标对象, 即被代理对象private Object target; public CGLIBInterceptor(Object target){this.target = target;} @Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodPro // 代理增强内容System.out.println("我是中介 , 开始代理"); //通过反射调⽤被代理类的⽅法Object retVal = methodProxy.invoke(target, objects); //代理增强内容System.out.println("我是中介 , 代理结束");return retVal; }}
创建代理类, 并使用
public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();HouseSubject proxy= (HouseSubject) Enhancer.create(target.getClass(),newproxy.rentHouse();}}
1.MethodInterceptor
MethodInterceptor和JDK动态代理中的InvocationHandler类似,它只定义了一个方法intercept(),用于增强目标方法.
public interface MethodInterceptor extends Callback {/*** 参数说明:* o: 被代理的对象* method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法) * objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法 */Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable; }
2. Enhancer.create()
Enhancer.create()用来生成一个代理对象
public static Object create(Class type, Callback callback) { //...代码省略}
参数说明:
type: 被代理类的类型(类或接口)
callback: ⾃定义⽅法拦截器 MethodInterceptor
3.总结
1.AOP是一种思想,是对某一类事情的集中处理.Spring框架实现了AOP,称之为SpringAOP
2.SpringAOP常见实现方式有两种:1.基于注解@Aspect来实现2.基于自定义注解来实现,还有一些更原始的方式,比如基于代理,基于xml配置的方式,但目标比较少见
3.SpringAOP是基于动态代理实现的,有两种方式:1.基本JDK动态代理实现2.基于CGLIB动态代理实现.运行时使用哪种方式与项目配置和代理的对象有关.
相关文章:

详解Spring AOP(二)
目录 1.切点表达式 1.1execution表达式 1.2 annotation 1.2.1自定义注解MyAspect 1.2.3添加自定义注解 2.Sping AOP原理 2.1代理模式 2.1.1静态代理 2.1.2动态代理 2.1.3JDK动态代理 2.1.4CGLIB动态代理 3.总结 承接上文:详解Spring AOP(一&…...

sql-analysis
文章目录 痛点: 1、无法提前发现慢sql,可能恶化为慢sql的语句 2、线上出现慢sql后,无法快速止损 后果:一般是以响应时间来发现慢sql,这时候已经对业务产生了一定影响,这时候就要改代码重新发布上线或者改数…...

后台管理台字典localStorage缓存删除
localStorage里存放了如以下dictItems_开头的字典数据,localStorage缓存是没有过期时间的,需要手动删除。同时localStorage里还存有其他不需要删除的数据。 这里的方案是遍历localStorage,利用正则和所有key进行匹配,匹配到dict…...

计算机毕业设计PySpark+Hadoop招聘推荐系统 招聘大数据 招聘数据分析 招聘可视化 大数据毕业设计 大数据毕设
1. 管理端: 带有职位的增删改查功能,评论功能是针对新闻模块的,类似新闻大数据的实现 2. 网站端: python / java 协同过滤推荐算法 / 下载职位数据表收费1元每条 / 账户充值 / 短信验证码修改密码 / 身份证识别 / 多条件搜索 3.…...

.Net预定义的泛型委托
我们每次要使用一个委托前,都需要自定义这个委托类型,声明其参数和返回值,然后才能实例化委托类型的对象、最后调用委托对象。 为了简化这个过程,.Net预定义了Func<T>委托、Action<T>委托类型和Predicate<T>&a…...

Unity的Excel转表工具
该Excel工具主要由Python语言完成,版本为3.x 主要功能: 1.转换后的数据存储结构为二进制。 2.excel文件可以选择多种数据类型:int、float、string、一维(int、float、string)、二维int、Map(int/int、in…...

静态随机存储器(SRAM):高速缓存的奥秘
目录 基本的静态存储单元阵列 基本的SRAM逻辑结构 1. 概述 2. SRAM阵列 3. 行选择器(Row Decoder) 4. 列选择器(Column Decoder) 5. 读写电路 6. 控制电路 7. 工作过程 SRAM的读/写时序 SRAM 结构概述 读操作时序 读…...

Linux CentOS 7 服务器集群硬件常用查看命令
(一)查看内核:uname -a [rootcdh1 ~]# uname -a Linux cdh1.macro.com 3.10.0-1062.el7.x86_64 #1 SMP Wed Aug 7 18:08:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux(二)查看系统:cat /etc/redhat-releas…...

《Windows API每日一练》5.4 键盘消息和字符集
本节我们将通过实例来说明不同国家的语言、字符集和字体之间的差异,以及Windows系统是如何处理的。 本节必须掌握的知识点: 第31练:显示键盘消息 非英语键盘问题 字符集和字体 第32练:显示默认字体信息 第33练:创建逻…...

【uniapp】uniapp开发微信小程序入门教程
HBuilderx中uniapp开发微信小程序入门教程 一、 环境搭建 1. HBuilderx下载安装 HBuilderx下载安装地址 2. 微信开发者工具下载安装 微信开发者工地址具下载安装 二、创建uniapp项目 选择:文件>新建>项目>uni-app 输入项目名称>选择默认模板>…...

Python爬虫项目集:豆瓣电影排行榜top250
关于整理日常练习的一些爬虫小练习,可用作学习使用。 爬取项目以学习为主,尽可能使用更多的模块进行练习,而不是最优解。 爬虫概要 示例python 库爬取模块request解析模块BeautifulSoup存储类型list(方便存入数据库)…...

34-Openwrt uhttpd与rpcd
uhttpd作为一个简单的web服务器,其代码量并不多,而且组织结构比较清楚。和其它网络服务器差不多,其main函数进行一些初始化(首先parse config-file,然后parse argv),然后进入一个循环࿰…...

uni app 树状结构数据展示
树状数据展示,可以点击item 将点击数据给父组件 ,满足自己需求。不喜勿喷,很简单可以根据自己需求改哈,不要问,点赞收藏就好。其实可以和上一篇文章uni app 自定义 带popup弹窗的input组件-CSDN博客结合使用ÿ…...

KVM在线yum源部署-centos 7
一、虚拟化简介 虚拟化就是操作系统里嵌套操作系统,一台服务器买回来,可能只是用作一个http服务,资源不能充分利用,而虚拟化的诞生有效解决了这个问题,以硬件资源上使用虚拟化,实现单硬件多系统,充分挖掘硬件性能,节能增效。同时通过多年的改进发展,虚拟化进化成云服务…...

TSF的服务发现与Consul有何区别?
TSF(腾讯服务框架)和Consul都是用于服务发现的工具,但它们在设计理念、功能特性、集成方式等方面存在一些区别。 ### 设计理念和目标 **Consul** 是一个开源的工具,用于服务发现、配置和分段。它提供了一种简单的方式来注册和发现服务,以及健康检查和键值存储功能。Consul…...

kotlin集合框架
1、集合框架的接口类型对比 2、不可变和可变List fun main() {// 不可变List - 不能删除或添加元素val intList: List<Int> listOf(1,2,3)intList.forEach{println(it) // 1 2 3}println("")// 可变List - 可以删除或添加元素val mutableList mutableListO…...

服务器(Linux系统的使用)——自学习梳理
root表示用户名 后是机器的名字 ~表示文件夹,刚上来是默认的用户目录 ls -a 可以显示出隐藏的文件 蓝色的表示文件夹 白色的是文件 ll -a 查看详细信息 total表示所占磁盘总大小 一般以KB为单位 d开头表示文件夹 -代表文件 后面得三组rwx分别对应管理员用户-组…...

竞赛选题 python+opencv+深度学习实现二维码识别
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 pythonopencv深度学习实现二维码识别 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:3分创新点:3分 该项目较为新颖&…...

Java读取指定 JAR 包路径中的 git.properties 文件
Java读取指定 JAR 包路径中的 git.properties 文件 在上述代码中,首先打开 JAR 文件,获取 git.properties 文件的 JarEntry 对象,如果存在该条目,就获取其输入流进行后续的读取和处理。具体的读取和处理逻辑需要根据您的实际需求在…...

逻辑回归(Logistic Regression)及其在机器学习中的应用
🚀时空传送门 🔍逻辑回归原理📕Sigmoid函数🎈逻辑回归模型 📕损失函数与优化🎈损失函数🚀优化算法 🔍逻辑回归的应用场景🍀使用逻辑回归预测客户流失使用scikit-learn库实…...

【计算机视觉】人脸算法之图像处理基础知识【七】
直方图均衡化 直方图均衡化是一种常用的图像处理技术,用于改善图像的对比度,特别是在图像的细节被埋没在暗部或亮部区域时。通过重新分配图像的像素强度值,使得图像的整体对比度增强,从而让更多的细节变得可见。 import cv2 imp…...

家政预约小程序14权限配置
目录 1 创建用户2 创建角色3 启用登录4 实现退出总结 我们现在小程序端的功能基本开发好了,小程序开发好之后需要给运营人员提供管理后台,要分配账号、配置权限,我们本篇就介绍一下权限如何分配。 1 创建用户 在微搭中,用户分为内…...

解决 vue 项目一直出现 sockjs-node/info?t=问题
其实如果是在开发环境,应该是开发的时候网络环境变更导致,比如你切换无线网络,导致开发服务器的IP地址换了,这样开发服务器会不知道如何确定访问源。开发环境中关闭npm dev server,然后重新npm run serve重新构建服务环…...

麒麟信安系统关闭core文件操作
在使用麒麟信安系统时,如果应用程序运行过程中崩溃了,此时并不会导致内核崩溃,只会在tmp目录下产生崩溃数据,如下图 不过tmp目录下的分区容量有限,当崩溃的应用core文件过大时将会占用tmp空间,导致tmpfs分区…...

微信小程序轮播图
效果图 详情可见 微信小程序 参照:swiper | uni-app官网 代码: <!--轮播图-- > <swiper interval"2000" autoplay"true" circular"true" style"height: 300px;"><swiper-item style&qu…...

redisson WRONGPASS invalid username-password pair or user is disable
1、技术架构:若依微服务框架 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2021.1</version></dependency> <dependency><…...

QT拖放事件之一:初识拖放4大事件处理函数
0、拖放 两个动作,合在一起称之为拖放事件; 拖:就是拖着走; 放:就是拖着走,然后松开鼠标了,释放了,这就是放; 注意:放:拖着的东西要放在什么地方??? 假如,我将一个记事本拖着跑,然后放到一个Widget窗口上,那么为了使得Widget能感知相应的事件(拖着进入事件…...

使用Python进行数据可视化:从基础到高级
使用Python进行数据可视化:从基础到高级 数据可视化是数据分析过程中不可或缺的一部分,通过图形化的方式展示数据,可以更直观地发现数据中的趋势和模式。Python凭借其丰富的库和强大的功能,成为数据可视化的首选编程语言。本文将介绍数据可视化的基础概念、常用的Python库…...

【十二】图解 Spring 核心数据结构:BeanDefinition
图解 Spring 核心数据结构:BeanDefinition 简介 使用spring框架的技术人员都知道spring两个大核心技术IOC和AOP,随着投入更多的时间去学习spring生态,越发觉得spring的发展不可思议,一直都是引领着Java EE的技术变革,这…...

速盾:阿里云ddos黑洞是怎么回事?
阿里云ddos黑洞是一种防御分布式拒绝服务(DDoS)攻击的安全机制。DDoS攻击是指利用大量的合法请求占用目标服务器的资源,从而使服务器无法正常响应合法用户的请求。为了应对这种攻击,阿里云引入了黑洞机制。 黑洞机制是一种主动防…...