[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;}
}

上述代码的测试结果如下:
在这里插入图片描述
我们看到了接口返回了执行耗时.
下面我们对上面的代码进行详细的解释:

  1. @Aspect : 表示的是切面.表示这个类是一个切面类.

  2. @Around: 环绕通知,在目标方法前后都会有代码执行.
    在这里插入图片描述

  3. proceedingJoinPoint.proceed(): 执行加入切面的连接点.

  4. execution(* com.jrj.books.controller.*.*(..))):切点表达式,表示这个切面对该项目下的那个方法生效.

  5. 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 "";}
}

下面我们来执行代码,观察后端日志:

  1. 正常执行的代码
    在这里插入图片描述
    在程序运行正常的情况下,@AfterThrowing注解的方法不会执行.
    从上面的执行结果中,我们可以看出:@Around有前置逻辑和后置逻辑两部分,这两个逻辑再内层就是@Before@After标识的方法,再往内层就是@AfterReturning标注的方法.
    在这里插入图片描述
  2. 异常的情况
    在这里插入图片描述
    在异常发生的情况下,@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 切点表达式

上面的代码中,我们一直在使用切点表达式来描述切点,下面我们来介绍一下切点表达式.
切点表达式最常见的有以下两种方式:

  1. execution(...): 根据方法的签名来匹配.
  2. @annotation:根据注解匹配.

3.5.1 execution表达式

execution(<访问限定符> <返回类型> <包名.类名.方法(方法参数)><异常>)
其中访问限定符可以省略.
在这里插入图片描述
切点表达式支持通配符:

  1. *:匹配任何字符,只可以匹配一个元素,可以匹配返回类型,包名,类名,方法名,方法参数.
    a. 包名使用* 表示任意包(⼀层包使用⼀个*)
    b. 类名使用* 表示任意类
    c. 返回值使用* 表示任意返回值类型
    d. 方法名使用* 表示任意方法
    e. 参数使用* 表示⼀个任意类型的参数
  2. ..:匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数.
    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注解来描述一类切点.具体实现步骤如下:

  1. 编写自定义注解
  2. 使用@annotation表达式来描述切点.
  3. 在连接点的方法上添加自定义注解.
  • 自定义注解
    创建一个自定义注解类,创建的方式和创建类是一样的.
    在这里插入图片描述
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {}
  1. @Target注解,代表的是注解可以修饰的对象,常用的有以下四种取值:
    ElementType.TYPE:用于描述类、接口(包括注解类型)或enum声明
    ElementType.METHOD:描述方法
    ElementType.PARAMETER:描述参数
    ElementType.TYPE_USE:可以标注任意类型
  2. @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 代理模式

代理模式,也叫委托模式
为其他对象提供⼀种代理以控制对这个对象的访问.它的作用就是通过提供⼀个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用.
在这里插入图片描述在这里插入图片描述

举例说明代理模式:
房屋中介与房东,艺人与经纪人,老板与秘书.

代理模式的主要角色:

  1. Subject:业务接口类.这里定义的是代理和被代理对象要做的事情.可以抽象类或者接口(不一定有).
  2. RealSubject:业务实现类.具体的业务执行,也就是被代理的对象.
  3. Proxy:代理类.RealSubject的代理.
    根据代理创建的时期,可以把代理模式分为动态代理静态代理.

4.1.1 静态代理

静态代理:在程序运行前,代理类的.class文件就已经存在了,代理类和被代理类就一定绑定好了固定的关系.(在出租房子之前,中介和房东已经做好了相关的工作就等租户来租房子了)

  1. 接口定义(定义房东和中介要做的事情)
public interface HouseSubject {void rentHouse();
}
  1. 实现接口(房东出租房子)
public class RealHouseProxy implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我要出租房子");}
}
  1. 代理(中介,帮房东出租房子)
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("代理完成");}
}
  1. 使用
public static void main(String[] args) {RealHouseProxy realHouseProxy1 = new RealHouseProxy();Proxy proxy = new Proxy(realHouseProxy1);proxy.rentHouse();
}

运行结果:
在这里插入图片描述
虽然静态代理也完成了代理,但是由于代码是写死的,对目标方法的增强都是手动来完成的,非常不灵活,所以我们有了动态代理.

4.1.2 动态代理

我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个代理对象的工作推迟到程序运行的时候由JVM来实现,也就是在程序运行的时候,根据需要动态创建代理.
常见的动态代理实现模式有两种方式:

  1. 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;//由于动态代理的代理对象不是固定的,所以这里使用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);//表示执行被代理对象中的方法,这里使用的是反射的机制//其中invoke就代表的是执行类中的方法,//invoke中第一个参数表示的是被代理类,第二个参数代表的是执行被代理类的方法的时候需要传入的参数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));//与静态代理不同的是,静态代理是在JVM运行之前就已经写好了,但是动态代理是在JVM运行的时候动态创建了一个代理proxy.rentHouse();}
}
[注意事项]
1. 在动态代理类中,由于代理的对象可以是任何类型的,所以被代理对象应该是Object类型的.
2. 在使用newInstance方法创建代理对象的时候,第一个传入的是被代理对象的加载器,第二个传入的是一个class类的数组,数组中放着被代理对象接口的class,最后一个放的是我们提前设定好的代理类的对象.
  1. 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代理模式非常类似.

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

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

相关文章

Java-数据结构-时间和空间复杂度

一、什么是时间和空间复杂度&#xff1f; &#x1f4da; 那么在了解时间复杂度和空间复杂度之前&#xff0c;我们先要知道为何有这两者的概念&#xff1a; 首先我们要先了解"算法"&#xff0c;在之前我们学习过关于"一维前缀和与差分"&#xff0c;"…

Cesium 实战 27 - 三维视频融合(视频投影)

Cesium 实战 27 - 三维视频融合(视频投影) 核心代码完整代码在线示例在 Cesium 中有几种展示视频的方式,比如墙体使用视频材质,还有地面多边形使用视频材质,都可以实现视频功能。 但是随着摄像头和无人机的流行,需要视频和场景深度融合,简单的实现方式则不能满足需求。…

U盘格式化工具合集:6个免费的U盘格式化工具

在日常使用中&#xff0c;U盘可能会因为文件系统不兼容、数据损坏或使用需求发生改变而需要进行格式化。一个合适的格式化工具不仅可以清理存储空间&#xff0c;还能解决部分存储问题。本文为大家精选了6款免费的U盘格式化工具&#xff0c;并详细介绍它们的功能、使用方法、优缺…

如何使用AI工具cursor(内置ChatGPT 4o+claude-3.5)

⚠️温馨提示&#xff1a; 禁止商业用途&#xff0c;请支持正版&#xff0c;充值使用&#xff0c;尊重知识产权&#xff01; 免责声明&#xff1a; 1、本教程仅用于学习和研究使用&#xff0c;不得用于商业或非法行为。 2、请遵守Cursor的服务条款以及相关法律法规。 3、本…

Spring Boot的开发工具(DevTools)模块中的热更新特性导致的问题

问题&#xff1a; java.lang.ClassCastException: class cn.best.scholarflow.framework.system.domain.entity.SysUser cannot be cast to class cn.best.scholarflow.framework.system.domain.entity.SysUser (cn.best.scholarflow.framework.system.domain.…

异常与中断(上)

文章目录 一、异常与中断的概念引入与处理流程1.1 生活中的中断1.2 母亲如何处理中断1.3 ARM系统中异常与中断处理流程 二、ARM架构中异常与中断的处理2.1 处理流程2.2 cortex M3/M42.2.1 M3/M4的向量表2.2.2 M3/M4的异常/中断处理流程 2.3 cortex A72.3.1 A7的向量表2.3.2 A7的…

Zabbix 监控平台 添加监控目标主机

Zabbix监控平台是一个企业级开源解决方案&#xff0c;用于分布式系统监视和网络监视。它由Zabbix Server和可选组件Zabbix Agent组成&#xff0c;通过C/S模式&#xff08;客户端-服务器模型&#xff09;采集数据&#xff0c;并通过B/S模式&#xff08;浏览器-服务器模型&#x…

游戏关卡设计的常用模式

游戏关卡分为很多种&#xff0c;但常用的有固定套路&#xff0c;分为若干种类型。 关卡是主角与怪物、敌方战斗的场所&#xff0c;包括装饰物、通道。 单人游戏的关卡较小&#xff0c;偏线性&#xff1b; 联机/MMO的关卡较大&#xff0c;通道多&#xff0c;自由度高&#xf…

【容器化技术 Docker 与微服务部署】详解

容器化技术 Docker 与微服务部署 一、容器化技术概述 &#xff08;一&#xff09;概念 容器化技术是一种操作系统级别的虚拟化方法&#xff0c;它允许将应用程序及其依赖项&#xff08;如运行时环境、系统工具、库等&#xff09;打包成一个独立的、可移植的单元&#xff0c;这…

QT集成IntelRealSense双目摄像头3,3D显示

前两篇文章&#xff0c;介绍了如何继承intel realsense相机和opengl。 这里介绍如何给深度数据和色彩数据一块显示到opengl里面。 首先&#xff0c;需要了解深度数据和彩色数据是如何存储的。先说彩色数据。彩色图像一般都是RGB&#xff0c;也就是每个像素有三个字节&#xf…

Postman[4] 环境设置

作用&#xff1a;不同的环境可以定义不同的参数&#xff0c;在运行请求时可以根据自己的需求选择需要的环境 1.创建Environment 步骤&#xff1a; Environment-> ->命名->添加环境变量 2.使用Environment 步骤&#xff1a;Collection- >右上角选择需要的环境

SpringBoot_第二天

SpringBoot_第二天 学习目标 Mybatis整合&数据访问 使用SpringBoot开发企业项目时&#xff0c;持久层数据访问是前端页面数据展示的基础&#xff0c;SpringBoot支持市面上常见的关系库产品(Oracle,Mysql,SqlServer,DB2等)对应的相关持久层框架&#xff0c;当然除了对于关系…

SparseViT:基于稀疏编码Transformer的非语义中心、参数高效的图像篡改定位

摘要 https://arxiv.org/pdf/2412.14598 非语义特征或语义无关特征&#xff0c;与图像上下文无关但对图像篡改敏感&#xff0c;被认为是图像篡改定位&#xff08;IML&#xff09;的重要证据。由于无法获得人工标签&#xff0c;现有工作依赖于手工方法提取非语义特征。手工非语…

【git】git生成rsa公钥的方法

git生成rsa公钥的方法 一&#xff0c;简介二&#xff0c;操作方法三&#xff0c;总结 一&#xff0c;简介 在工作的过程中&#xff0c;经常需要生成rsa的密钥&#xff0c;然后提供给别人&#xff0c;然后别人给你开通代码下载权限。本文介绍如何在本地生成rsa的密钥供参考。 …

【高项】信息系统项目管理师(二)项目管理概论

一、PMBOK的发展 项目管理知识体系&#xff08;PMBOK&#xff09;是由美国项目管理协会&#xff08;PMI&#xff09;开发的一套描述项目管理专业范围的知识体系&#xff0c;包含了对项目管理所需的知识、技能和工具的描述。 二、项目基本要素 2.1 项目基础 项目是为提供一项…

数据中台与数据治理服务方案[50页PPT]

本文概述了数据中台与数据治理服务方案的核心要点。数据中台作为政务服务数据化的核心&#xff0c;通过整合各部门业务系统数据&#xff0c;进行建模与加工&#xff0c;以新数据驱动政府管理效率提升与政务服务能力增强。数据治理则聚焦于解决整体架构问题&#xff0c;确保数据…

程序员测试日常小工具

作为一名程序员&#xff0c;或者测试人员&#xff0c;日常工作最常用的工具有哪些&#xff0c;截图&#xff0c;截图漂浮&#xff0c;翻译&#xff0c;日期处理&#xff0c;api调用...&#xff0c; 当你拿到一串报文后&#xff0c;想要json转换时&#xff0c;是不是要打…

【MySQL高级】第1-4章

第1章 存储过程 1.1 什么是存储过程&#xff1f; 存储过程可称为过程化SQL语言&#xff0c;是在普通SQL语句的基础上增加了编程语言的特点&#xff0c;把数据操作语句(DML)和查询语句(DQL)组织在过程化代码中&#xff0c;通过逻辑判断、循环等操作实现复杂计算的程序语言。 换…

获取用户详细信息-ThreadLocal优化

Thread全局接口可用&#xff0c;不用再重复编写。所以为了代码的复用&#xff0c;使用Thread。把之前的内容&#xff08;函数的参数和map与username&#xff09;注释掉&#xff0c;换为Thread传过来的内容&#xff08;map与username&#xff09;。 因为Thread需要在拦截器里面…

THUCNews解压/THUCNews数据集解压出问题

省流&#xff1a;使用zip64进行解压&#xff0c;文件数目太多windows默认zip16装不下 我在使用THUCNews中文文本数据集时出现了问题&#xff0c;原数据集解压后应该包含以下两个文件夹: 其中THUCNews文件夹下有以新闻类别命名的子文件。官网下载的是一个1.56GB的zip压缩包 而我…