Spring AOP 概念及其使用

news2024/11/28 4:40:56

目录

AOP概述

什么是AOP?

什么是Spring AOP ?

Spring AOP 快速入门

1.引⼊ AOP 依赖

2.编写AOP程序

Spring AOP 核心概念 

1.切点

2.连接点

3.通知

4.切面

通知类型

注意事项:

@PointCut(定义切点)

切面优先级 @Order

切点表达式:

execution表达式

切点表达式示例

@annotation

1. 编写自定义注解

2. 使⽤ @annotation 表达式来描述切点


AOP概述

        AOP 是 Spring 框架的第⼆⼤核⼼(第⼀⼤核⼼是 IoC 推荐看Spring IoC & DI 使⽤)

什么是AOP?

        Aspect Oriented Programming(⾯向切⾯编程)

        什么是⾯向切⾯编程呢? 切⾯就是指某⼀类特定问题,所以 AOP 也可以理解为⾯向特定⽅法编程. 什么是⾯向特定⽅法编程呢?⽐如"登录校验",就是⼀类特定问题.登录校验拦截器,就 是对"登录校验"这类问题的统⼀处理(推荐看拦截器使用详解).所以,拦截器也是 AOP 的⼀种应⽤. AOP 是⼀种思想,拦截器是 AOP  思想的⼀种实现. Spring 框架实现了这种思想, 提供了拦截器技术的相关接⼝. 同样的,统⼀数据返回格式和统⼀异常处理(推荐看Spring Boot 统⼀数据返回格式), 也是 AOP 思想的⼀种实现. 简单来说: AOP是⼀种思想,是对某⼀类事情的集中处理.

         AOP的作⽤:可以在不修改源代码的基础上对已有⽅法进⾏增强(⽆侵⼊性:解耦)

什么是Spring AOP ?

        AOP 是⼀种思想,它的实现⽅法有很多,有 Spring AOP,也有AspectJ、CGLIB等. Spring AOP是其中的⼀种实现⽅式.

Spring AOP 快速入门

1.引⼊ AOP 依赖

        在pom.xml ⽂件中添加配置

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.编写AOP程序

@Aspect //标识这是一个切面类
@Component  //将 AspectDemo 的对象交给 Spring 进行管理,切面类中的通知方法才能对指定的目标方法起作用
@Slf4j
public class AspectDemo {
    //环绕通知(@Around)
    //通知方法作用于目标方法执行前和执行后
    @Around("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    // point 参数表示目标方法
    public Object around_AOP(ProceedingJoinPoint point) throws Throwable {
        log.info("执行 around_AOP 方法,目标方法前");
        //执行目标方法,并且要用一个变量接收目标方法的值,然后再让通知方法返回
        Object result=point.proceed();
        log.info("执行 around_AOP 方法,目标方法后");
        return result;
    }
}

        对于上述程序进行简单讲解:

        @Aspect:标识这是⼀个切⾯类

        @Component:IoC 核心思想,五大注解其中之一,表示将 AspectDemo 这个切面类的对象交给 Spring 进行管理,要加上这个注解,切面类中的通知方法才会对指定的目标方法起作用

        @Slf4j:lombok 工具类提供的注解,为我们创建了一个日志对象 log ,用于打印日志的

        @Around:环绕通知,在⽬标⽅法的前后都会被执⾏.后⾯的表达式表⽰对哪些⽅法进⾏增强.(指定目标方法)

        ProceedingJoinPoint 类型的对象 point 代表目标方法,point.proceed() 代表执行目标方法

Spring AOP 核心概念 

1.切点

        切点(Pointcut), 也称之为"切⼊点"  Pointcut 的作⽤就是提供⼀组规则,告诉程序对哪些⽅法来进⾏功能增强,简单来说:切点就是通过表达式指定对哪些目标方法进行功能增强,上⾯的表达式execution(* com.yulin.spring_aop_demo.Controller.*.*(..))  就是切点表达式.

2.连接点

        满⾜切点表达式规则的⽅法,就是连接点.简单来说:每一个需要进行功能增强的目标方法都是连接点,在上述代码中 com.yulin.spring_aop_demo.Controller 路径下的所有目标方法都是连接点

        切点和连接点的关系:连接点是满⾜切点表达式的元素.切点可以看做是保存了众多连接点的⼀个集合,简单来说:切点表示所有要进行功能增强的目标方法,每个目标方法都是一个连接点

3.通知

         通知就是具体要做的⼯作,指那些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法),简单来说:我们想要为目标方法添加的新功能就是通知,新功能的代码写在一个方法中,这个方法叫通知方法

4.切面

        切⾯(Aspect)= 切点 (Pointcut) + 通知(Advice)

        通过切⾯就能够描述当前 AOP 程序需要针对于哪些⽅法,添加哪些新的功能,如下图,一个完整的通知方法就是一个切面,通知方法所在的类就叫切面类

通知类型

        上⾯我们讲了什么是通知,接下来学习通知的类型.@Around 就是其中⼀种通知类型,表⽰环绕通知. 

        Spring AOP的通知类型有以下⼏种:

         • @Around:环绕通知,此注解标注的通知⽅法在⽬标⽅法前,后都被执⾏ 

        • @Before:前置通知,此注解标注的通知⽅法在⽬标⽅法前被执⾏

        • @After: 后置通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,⽆论是否有异常都会执⾏

        • @AfterReturning: 返回后通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,有异常不会执⾏ 

        • @AfterThrowing: 异常后通知, 此注解标注的通知⽅法在目标方法发⽣异常后执⾏

        我们可以通过如下的代码来熟悉这几种通知类型:

@Aspect //标识这是一个切面类
@Component  //将 AspectDemo 的对象交给 Spring 进行管理,切面类中的通知方法才能对指定的目标方法起作用
@Slf4j
public class AspectDemo {
    //前置通知(@Before)
    //通知方法作用于目标方法执行前
    @Before("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    public void before_AOP(){
        log.info("执行 before_AOP 方法");
    }

    //后置通知(@After)
    //通知方法作用于目标方法执行后
    @After("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    public void after_AOP(){
        log.info("执行 after_AOP 方法");
    }

    //返回后通知(@AfterReturning)
    //通知方法作用于目标方法“正确”返回后
    @AfterReturning("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    public void afterReturning_AOP(){
        log.info("执行 afterReturning_AOP 方法");
    }

    //抛出异常后通知(@AfterThrowing)
    //通知方法作用于目标方法抛出异常后
    @AfterThrowing("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    public void afterThrowing_AOP(){
        log.info("执行 afterThrowing_AOP 方法");
    }

    //环绕通知(@Around)
    //通知方法作用于目标方法执行前和执行后
    @Around("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    // point 参数表示目标方法
    public Object around_AOP(ProceedingJoinPoint point) throws Throwable {
        log.info("执行 around_AOP 方法,目标方法前");
        //执行目标方法,并且要用一个变量接收目标方法的值,然后再让通知方法返回
        Object result=point.proceed();
        log.info("执行 around_AOP 方法,目标方法后");
        return result;
    }
}

        1.当 com.yulin.spring_aop_demo.Controller 路径下的一个目标方法正常执行时:

        程序正常运⾏的情况下, @AfterThrowing 标识的通知⽅法不会执⾏ 从上图也可以看出来, @Around 标识的通知⽅法包含两部分,⼀个"前置逻辑",⼀个"后置逻辑".其 中"前置逻辑"会先于 @Before 标识的通知⽅法执⾏,"后置逻辑"会晚于 @After 标识的通知⽅法执行

        2.当 com.yulin.spring_aop_demo.Controller 路径下的一个目标方法执行出现异常时:

·        @AfterReturning 标识的通知⽅法不会执⾏, @AfterThrowing 标识的通知⽅法执⾏了

@Around 环绕通知的后置逻辑没有执行,因为目标方法没有正常返回

注意事项:

        1.@Around 环绕通知需要调⽤ ProceedingJoinPoint.proceed() 来让原始⽅法执⾏,其他 通知不需要考虑⽬标⽅法执⾏.在该代码前的代码就是环绕通知的前置逻辑,该代码后的代码就是环绕通知的后置逻辑

        2.@Around 环绕通知⽅法的返回值,必须指定为 Object ,来接收目标⽅法的返回值,否则目标⽅法执⾏完毕,是获取不到返回值的.

@PointCut(定义切点)

       上面的代码有一个问题,就是存在⼤量重复的切点表达式 execution(* com.yulin.spring_aop_demo.Controller.*.*(..)) Spring提供了 @PointCut 注解,把公共的切点表达式提取出来,需要⽤到时引⽤该切⼊点表达式即可

        上面的代码可以修改为:

@Aspect //标识这是一个切面类
@Component  //将 AspectDemo 的对象交给 Spring 进行管理,切面类中的通知方法才能对指定的目标方法起作用
@Slf4j
public class AspectDemo {
    //定义切点
    @Pointcut("execution(* com.yulin.spring_aop_demo.Controller.*.*(..))")
    private void pt(){}
    //前置通知(@Before)
    //通知方法作用于目标方法执行前
    @Before("pt()")
    public void before_AOP(){
        log.info("执行 before_AOP 方法");
    }

    //后置通知(@After)
    //通知方法作用于目标方法执行后
    @After("pt()")
    public void after_AOP(){
        log.info("执行 after_AOP 方法");
    }

    //返回后通知(@AfterReturning)
    //通知方法作用于目标方法“正确”返回后
    @AfterReturning("pt()")
    public void afterReturning_AOP(){
        log.info("执行 afterReturning_AOP 方法");
    }

    //抛出异常后通知(@AfterThrowing)
    //通知方法作用于目标方法抛出异常后
    @AfterThrowing("pt()")
    public void afterThrowing_AOP(){
        log.info("执行 afterThrowing_AOP 方法");
    }

    //环绕通知(@Around)
    //通知方法作用于目标方法执行前和执行后
    @Around("pt()")
    // point 参数表示目标方法
    public Object around_AOP(ProceedingJoinPoint point) throws Throwable {
        log.info("执行 around_AOP 方法,目标方法前");
        //执行目标方法,并且要用一个变量接收目标方法的值,然后再让通知方法返回
        Object result=point.proceed();
        log.info("执行 around_AOP 方法,目标方法后");
        return result;
    }
}

        注意:当切点定义使⽤private修饰时,仅能在当前切⾯类中使⽤,当其他切⾯类也要使⽤当前切点定义时,就需要把private改为public.引⽤⽅式为:全限定类名.⽅法名 ()

切面优先级 @Order

        当我们在⼀个项⽬中,定义了多个切⾯类时,并且这些切⾯类的切入点都匹配到了同⼀个⽬标⽅法.当⽬标⽅法运⾏的时候,这些切⾯类中的通知⽅法都会执⾏,那么这⼏个通知⽅法的执⾏顺序是什么样的呢?

        我们通过程序来求证:

        定义多个切⾯类: AspectDemo1 和 AspectDemo2,为简单化,只写了 @Before 和 @After 两个通知

AspectDemo1:

@Aspect
@Component
@Slf4j
public class AspectDemo1 {
    @Before("com.yulin.spring_aop_demo.Aspect.AspectDemo.pt()")
    public void before_AOP(){
        log.info("执行 AspectDemo1 -》 before_AOP 方法");
    }

    @After("com.yulin.spring_aop_demo.Aspect.AspectDemo.pt()")
    public void after_AOP(){
        log.info("执行 AspectDemo1 -》 after_AOP 方法");
    }

}

AspectDemo2:

@Aspect
@Component
@Slf4j
public class AspectDemo2 {
    @Before("com.yulin.spring_aop_demo.Aspect.AspectDemo.pt()")
    public void before_AOP(){
        log.info("执行 AspectDemo2 -》 before_AOP 方法");
    }

    @After("com.yulin.spring_aop_demo.Aspect.AspectDemo.pt()")
    public void after_AOP(){
        log.info("执行 AspectDemo2 -》 after_AOP 方法");
    }

}

        上述两个切面类中的通知方法都作用于同一批目标方法,我们现在来观察,目标方法执行后,通知方法的执行顺序

        

2023-12-04 11:55:07.539  INFO 28864 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo1   : 执行 AspectDemo1 -》 before_AOP 方法
2023-12-04 11:55:07.539  INFO 28864 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo2   : 执行 AspectDemo2 -》 before_AOP 方法
2023-12-04 11:55:07.545  INFO 28864 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo2   : 执行 AspectDemo2 -》 after_AOP 方法
2023-12-04 11:55:07.545  INFO 28864 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo1   : 执行 AspectDemo1 -》 after_AOP 方法

        如上的日志可以看出:

        存在多个切⾯类时,默认按照切⾯类的类名字⺟排序: 

        • @Before 通知:字⺟排名靠前的先执⾏

        • @After 通知:字⺟排名靠前的后执⾏

        但这种⽅式不⽅便管理,我们的类名更多还是具备⼀定含义的,这种方式不可控,Spring 给我们提供了⼀个新的注解, 来控制这些切⾯通知的执⾏顺序: @Order

使⽤⽅式如下:

@Aspect
@Component
@Order(2)
public class AspectDemo1 {
 //...代码省略 
}
@Aspect
@Component
@Order(1)
public class AspectDemo2 {
 //...代码省略 
}

        设置优先级以后,重新访问目标方法,带动执行通知方法,得到的日志为:

2023-12-04 12:07:38.540  INFO 34560 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo2   : 执行 AspectDemo2 -》 before_AOP 方法
2023-12-04 12:07:38.541  INFO 34560 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo1   : 执行 AspectDemo1 -》 before_AOP 方法
2023-12-04 12:07:38.546  INFO 34560 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo1   : 执行 AspectDemo1 -》 after_AOP 方法
2023-12-04 12:07:38.546  INFO 34560 --- [nio-8080-exec-1] c.y.spring_aop_demo.Aspect.AspectDemo2   : 执行 AspectDemo2 -》 after_AOP 方法

        当我们设置 AspectDemo2 的优先级为1,AspectDemo1 的优先级为 2 后,AspectDemo2 的优先级就比 AspectDemo1 的高,所以 AspectDemo2 的 before 通知会先执行,after 通知会后执行

        通过上述程序的运⾏结果,得出结论: 

        @Order 注解标识的切⾯类,执⾏顺序如下:

         • @Before 通知:数字越⼩,优先级越高先执⾏

         • @After 通知:数字越⼤,优先级越小先执⾏

切点表达式:

        上⾯的代码中,我们⼀直在使⽤切点表达式来描述切点(描述目标方法所在的位置).下⾯我们来介绍⼀下切点表达式的语法. 切点表达式常⻅有两种表达⽅式

1. execution(......) 根据⽅法的签名来匹配

2.@annotation(......) 根据注解匹配

execution表达式

        execution() 是最常⽤的切点表达式, ⽤来匹配目标⽅法,语法为:

execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>),其中:访问修饰符和异常可以省略

访问修饰符:为 private ,public ,protected,匹配目标方法的访问修饰符

切点表达式⽀持通配符表达:

1. * :匹配任意字符,只匹配⼀个元素(返回类型,包,类名,⽅法或者⽅法参数) 

        a. 包名使⽤* 表⽰任意包(⼀层包使⽤⼀个*)

        b. 类名使⽤* 表⽰任意类 

        c. 返回值使⽤* 表⽰任意返回值类型 

        d. ⽅法名使⽤* 表⽰任意⽅法 

        e. 参数使⽤* 表⽰⼀个任意类型的参数

.. :匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数

        a. 使⽤ .. 配置包名,标识此包以及此包下的所有⼦包

        b. 可以使⽤ .. 配置参数,匹配任意个任意类型的参数

切点表达式示例

TestController类下的 public修饰,返回类型为String⽅法名为t1,⽆参⽅法

execution(public String com.example.demo.controller.TestController.t1())

匹配 controller 包下所有的类的所有⽅法

execution(* com.example.demo.controller.*.*(..))

匹配所有包下⾯的 TestController 类中的所有方法

execution(* com..TestController.*(..))

匹配com.example.demo包下,⼦孙包下的所有类的所有⽅法

execution(* com.example.demo..*(..))

@annotation

        execution 表达式更适⽤有规则的,如果我们要匹配多个⽆规则的⽅法呢,⽐如:TestController中的 t1()  和 UserController 中的 u1() 这两个⽅法.

        这个时候我们使⽤ execution 这种切点表达式来描述就不是很⽅便了.我们可以借助⾃定义注解的⽅式以及另⼀种切点表达式 @annotation 来描述这⼀类的切点

       实现步骤:

        1. 编写⾃定义注解

        2. 使⽤ @annotation 表达式来描述切点

        3. 在连接点的⽅法上添加⾃定义注解

1. 编写自定义注解

        创建⼀个注解类(和创建 Class ⽂件⼀样的流程,选择Annotation就可以了)

@Target(ElementType.METHOD) //表示自定义注解的作用范围,即该注解可以⽤在什么地⽅.
@Retention(RetentionPolicy.RUNTIME) //表示自定义注解的生命周期,即注解被保留的时间⻓短
public @interface MyAspectAnnotation { }

        @Target 注解,用于设置自定义注解的作用范围,即该注解可以⽤在什么地⽅.上述代码表示注解用于方法上

        常⽤取值:

        ElementType.TYPE:⽤于描述类、接⼝(包括注解类型)或 enum 声明 

        ElementType.METHOD:描述⽅法

        ElementType.PARAMETER:描述参数

        ElementType.TYPE_USE:可以标注任意类型

        @Retention 注解,设置自定义注解的生命周期,即注解被保留的时间⻓短,上述代码表示注解在程序运行时也一直存在

        常⽤取值:

        1. RetentionPolicy.SOURCE:表⽰注解仅存在于源代码中,编译成字节码后会被丢弃.这意味着 在运⾏时⽆法获取到该注解的信息,只能在编译时使⽤.⽐如 @SuppressWarnings ,以及 lombok提供的注解 @Data ,@Slf4j

        2. RetentionPolicy.CLASS:编译时注解.表⽰注解存在于源代码和字节码中,但在运⾏时会被丢弃.这意味着在编译时和字节码中可以通过反射获取到该注解的信息,但在实际运⾏时⽆法获取.通常⽤于⼀些框架和⼯具的注解.

        3. RetentionPolicy.RUNTIME:运⾏时注解.表⽰注解存在于源代码,字节码和运⾏时中.这意味 着在编译时,字节码中和实际运⾏时都可以通过反射获取到该注解的信息.通常⽤于⼀些需要在运⾏时处理的注解,如Spring的 @Controller @ResponseBody

2. 使⽤ @annotation 表达式来描述切点

        我更喜欢这样理解:通过 @annotation 注解把切面类中的通知方法依附于注解上,在目标方法上使用该注解,在运行目标方法时便会执行依附于注解上的通知方法

@Aspect
@Component
@Slf4j
public class MyAspectDemo {
    // @annotation 注解中的参数是通知方法要绑定的注解位置
    //如下代码就将 beforeAOP()通知方法绑定到了 MyAspectAnnotation 注解上
    @Before("@annotation(com.yulin.spring_aop_demo.Annotation.MyAspectAnnotation)")
    public void beforeAOP(){
        log.info("执行 MyAspectDemo -》 beforeAOP 通知方法");
    }
    @After("@annotation(com.yulin.spring_aop_demo.Annotation.MyAspectAnnotation)")
    public void afterAOP(){
        log.info("执行 MyAspectDemo -》 beforeAOP 通知方法");
    }
}

        我们定义一个接口类来使用定义好的注解 @MyAspectAnnotation 

@RequestMapping("/userController")
@RestController
@Slf4j
public class userController {
    @RequestMapping("/getUserName")
    public String getUserName(){
        log.info("进入接口 getUserName ");
        return "小三";
    }
    @MyAspectAnnotation
    @RequestMapping("/getPassword")
    public String getPassword(){
        log.info("进入接口 getPassword ");
        return "123";
    }
}

        当我们访问 ”/userController/getUserName“ 路径,得到如下的日志

2023-12-04 16:33:01.873  INFO 18364 --- [nio-8080-exec-1] c.y.s.Controller.userController          : 进入接口 getUserName 

        通过日志我们可以看出,由于 getUserName()方法上没有添加 @MyAspectAnnotation 注解,所以访问该接口后,不会执行通知方法

         当我们访问 ”/userController/getPassword“ 路径,得到如下的日志

2023-12-04 16:35:47.123  INFO 18364 --- [nio-8080-exec-4] c.y.spring_aop_demo.Aspect.MyAspectDemo  : 执行 MyAspectDemo -》 beforeAOP 通知方法
2023-12-04 16:35:47.123  INFO 18364 --- [nio-8080-exec-4] c.y.s.Controller.userController          : 进入接口 getPassword 
2023-12-04 16:35:47.123  INFO 18364 --- [nio-8080-exec-4] c.y.spring_aop_demo.Aspect.MyAspectDemo  : 执行 MyAspectDemo -》 beforeAOP 通知方法

        通过日志我们可以看出,由于 getPassword()方法上添加了 @MyAspectAnnotation 注解,所以访问该接口后,会执行依附于该注解的通知方法

        Spring AOP的实现⽅式(常⻅⾯试题) 

        1. 基于注解 @Aspect (参考上述内容) 

        2. 基于⾃定义注解(参考⾃定义注解 @annotation 部分的内容)

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

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

相关文章

IDEA删除最近打开的文件记录

IDEA删除最近打开的文件记录 遇见问题&#xff1a;如何删除IDEA中最近打开的文件记录 解决方法 先关闭IDEA 找到 recentProjects.xml 文件 windows 位置&#xff1a;&#xff08;AppData是隐藏文件夹&#xff09; 1.C:\Users\电脑用户名\AppData\Roaming\JetBrains\IntelliJIde…

Bash脚本调用百度翻译API进行中文到英文的翻译

写一个bash脚本调用百度翻译API进行中文到英文的翻译&#xff0c;首先需要进行相关的申请。看百度给出的文档链接: 百度翻译API文档 需要先注册一个百度账号&#xff0c;然后申请APPID。脚本中会用到appid和key这两个值。按照文档给出的提示可以获得。如下是脚本&#xff1a; #…

零基础如何入门HarmonyOS开发?

HarmonyOS鸿蒙应用开发是当前非常热门的一个领域&#xff0c;许多人都想入门学习这个技术。但是&#xff0c;对于零基础的人来说&#xff0c;如何入门确实是一个问题。下面&#xff0c;我将从以下几个方面来介绍如何零基础入门HarmonyOS鸿蒙应用开发学习。 一、了解HarmonyOS鸿…

markdown记录

文章目录 基础操作使用一级列表、二级列表 博文链接 基础操作 使用一级列表、二级列表 博文链接 CSDN-Markdown语法集锦 CSDN-markdown语法之如何使用LaTeX语法编写数学公式 CSDN Markdown简明教程1-关于Markdown CSDN Markdown简明教程2-基本使用 CSDN Markdown简明教程3-表…

X86汇编语言:从实模式到保护模式(代码+注释)--c6

X86汇编语言&#xff1a;从实模式到保护模式&#xff08;代码注释&#xff09;–c6 标志寄存器FLAGS&#xff1a; 6th&#xff1a;ZF位&#xff08;Zero Flag&#xff09;&#xff1a;零标志&#xff0c;执行算数或者逻辑运算之后&#xff0c;会将该位置位。10th&#xff1a;D…

油猴(Tampermonkey)浏览器插件简单自定义脚本开发

介绍 浏览器插件&#xff0c;包括油猴插件和其他插件&#xff0c;通过它们可以实现浏览器网页的定制化与功能增强。 其他插件一般只有某种具体的功能&#xff0c;且已经写死而不能更改&#xff0c;比如Adblock插件只用于去广告。 油猴插件是一款用于管理用户脚本的插件&…

RocketMQ - SpringBoot整合RocketMQ

SpringBoot整合RocketMQ 1、快速实战 ​ 按照SpringBoot三板斧&#xff0c;快速创建RocketMQ的客户端。创建Maven工程&#xff0c;引入关键依赖&#xff1a; <dependencies><dependency><groupId>org.apache.rocketmq</groupId><artifactId>r…

HarmonyOS应用开发工具DevEco Studio安装与使用

语雀知识库地址&#xff1a;语雀HarmonyOS知识库 飞书知识库地址&#xff1a;飞书HarmonyOS知识库 知识库内容逐步完善中… 工欲善其事必先利其器&#xff0c;要编写HarmonyOS应用就需要用到官方提供的IDE工具来编写相应的代码。 在鸿蒙开发者官网&#xff0c;其提供了官方的开…

Markdown从入门到精通

Markdown从入门到精通 文章目录 Markdown从入门到精通前言一、Markdown是什么二、Markdown优点三、Markdown的基本语法3.1 标题3.2 字体3.3 换行3.4 引用3.5 链接3.6 图片3.7 列表3.8 分割线3.9 删除线3.10 下划线3.11 代码块3.12 表格3.13 脚注3.14 特殊符号 四、Markdown的高…

Ubuntu 22.04源码安装yasm 1.3.0

sudo lsb_release -r看到操作系统的版本是22.04&#xff0c;sudo uname -r可以看到内核版本是5.15.0-86-generic&#xff0c;sudo gcc --version可以看到版本是11.2.0&#xff0c;sudo make --version可以看到版本是GNU Make 4.3。 下载yasm http://yasm.tortall.net/Downlo…

深入了解Java 8日期时间新玩法:DateTimeFormatter与ZoneOffset的使用

推荐语 在这篇文章中&#xff0c;我们将深入探讨Java中的DateTimeFormatter和ZoneOffset类的功能和使用方法。这些类是在Java 8中引入的新的日期时间API的一部分&#xff0c;它们为我们提供了更灵活、更易用的日期和时间处理能力。尽管这些类在Java 8中已经出现&#xff0c;但…

SIT3232E高静电防护,单电源供电,双通道,RS232 收发器

SIT3232E 是一款 3.0V~5.5V 供电、双通道、低功耗、高静电防护 ESD 保护&#xff0c;完全满足 TIA/EIA-232 标准要求的 RS-232 收发器。 SIT3232E 包括两个驱动器和两个接收器&#xff0c;具有增强形 ESD 保护功能&#xff0c;达到 15kV 以上 HBM ESD 、 8kV …

拼多多商品详情数据接口在数据分析行业的作用性

在数据分析行业中&#xff0c;拼多多商品详情数据的作用性主要体现在以下几个方面&#xff1a; 了解市场和用户需求&#xff1a;通过拼多多商品详情数据&#xff0c;企业可以了解到市场上什么产品受欢迎&#xff0c;用户对产品的反馈和评价如何&#xff0c;从而调整自己的销售…

Qt/QML编程学习之心得:工程中的文件(十二)

Qt生成了工程之后,尤其在QtCreator产生对应的project项目之后,就如同VisualStudio一样,会产生相关的工程文件,那么这些工程文件都是做什么的呢?这里介绍一下。比如产生了一个Qt Widget application,当然如果Qt Quick Application工程会有所不同。 一、.pro和.pro.user …

神经内科临床常用的焦虑、抑郁评估量表,医生必备!

根据神经内科医生的量表使用情况&#xff0c;常笑医学整理了神经内科临床上常用的焦虑、抑郁评估量表&#xff0c;为大家分享临床常见的焦虑、抑郁、睡眠等量表评估内容&#xff0c;均支持量表下载和在线使用&#xff0c;建议收藏&#xff01; 1.汉密顿抑郁量表&#xff08;Ham…

初识Linux:权限(1)

目录 提示&#xff1a;以下指令均在Xshell 7 中进行 Linux 的权限 内核&#xff1a; 查看操作系统版本 查看cpu信息 查看内存信息 外部程序&#xff1a; 用户&#xff1a; 普通用户变为超级用户&#xff1a; su 和 su-的区别&#xff1a; root用户变成普通用户&#…

一文搞懂全连接算法和它的作用

如果你是搞AI算法的同学&#xff0c;相信你在很多地方都见过全连接层。 无论是处理图片的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;还是处理文本的自然语言处理&#xff08;NLP&#xff09;网络&#xff0c;在网络的结尾做分类的时候&#xff0c;总是会出现一个全…

chfs,简单好用的局域网共享网盘

1. 网盘简介 2. 下载安装包 点击地址下载 3. 部署网盘 &#xff08;1&#xff09;创建文件夹 mkdir -p /opt/chfs/share /opt/chfs/logs share 共享文件夹&#xff0c;也就是存放文件的地方 logs 存放日志文件&#xff08;2&#xff09;上传压缩包到 /opt/chfs目录下…

3dMax vs Cinema4d哪个更好更适合你?

Cinema 4d和3dMax的区别 用于游戏风格、开发和风格可视化的3D建模、动画和渲染软件系统&#xff0c;为用户提供制作和编辑动画、视觉效果和环境的灵活性。4D CINEMA可能是由MAXON构建的强大的3D建模、运动图形、绘画和动画软件系统。Cinema 4D将在每个Windows和MAC操作系统上运…

【UE5】监控摄像头效果(上)

目录 效果 步骤 一、视角切换 二、摄像头画面后期处理 三、在场景中显示摄像头画面 效果 步骤 一、视角切换 1. 新建一个Basic关卡&#xff0c;添加第三人称游戏资源到项目浏览器 2. 新建一个Actor蓝图&#xff0c;这里命名为“BP_SecurityCamera” 打开“BP_Securit…