Spring AOP(1)

news2024/11/15 8:26:23

目录

一、AOP 概述

什么是Spring AOP?

二、Spring AOP 快速入门

1、引入AOP依赖

2、编写AOP程序

三、Spring AOP 详解

1、Spring AOP的核心概念

(1)切点(Pointcut)

(2)连接点(Join Point)

(3)通知(Advice)

(4)切面(Aspect)

(5)总结

2、通知类型

(1)正常运行的情况

(2)运行失败的情况

(3)@Around 通知类型

(4)注意事项

3、@PointCut

4、切面优先级 @Order

5、切点表达式

(1)execution 表达式

切点表达式

(2)@annotation

① 自定义注解 @MyAspect

② 切面类

③ 添加自定义注解

6、Spring AOP 的实现方式(常见面试题)


一、AOP 概述

        AOP 是 Spring 框架 的第二大核心(第一大核心是 IoC)。

        AOP 的全称Aspect Oriented Programming(面向切面编程)什么是面向切面编程呢?切面就是指某一类特定问题,所以 AOP 也可以理解为 面向特定方法编程

        什么是面向特定方法编程呢?比如之前图书馆管理系统的 “登录校验”,就是一类特定问题。登录校验拦截器,就是对 “登录校验” 这类问题的统一处理。所以,拦截器也是 AOP 的一种应用。AOP 是一种思想,拦截器是 AOP 思想的一种实现。Spring 框架实现了这种思想,提供了拦截器技术的相关接口。

        同样的,统一数据返回格式 和 统一异常处理,也是 AOP 思想的一种实现。

        简单来说:AOP 是一种思想,是对某一类事情的集中处理

什么是Spring AOP?

        AOP 是一种思想,它的实现方法有很多,有Spring AOP,也有 AspectJ、CGLIB等。

        Spring AOP 是其中的一种实现方式。学会了统一功能之后,是不是就学会了Spring AOP 呢?当然不是。

        拦截器作用的维度是 URL(依次请求和响应),@ControllerAdvice 应用场景主要是全局异常处理(配合自定义异常效果更佳),数据绑定,数据预处理。AOP作用的维度更加细致(可以根据包、类、方法名、参数等进行拦截),能够实现更加复杂的业务逻辑。

        举个例子:我们现在有一个项目,项目中开发了很多功能。如图:

        现在有一些业务的执行效率比较低,耗时较长,我们需要对接口进行优化。第一步就需要定位出执行耗时比较长的业务方法,再针对该业务方法来进行优化。如何定位呢?我们就需要统计当前项目中每一个业务方法的执行耗时。

        如何统计呢?可以在业务方法运行前和运行后,记录下方法的开始时间和结束时间,两者之差就是这个方法的耗时。如图:

        上面这种方法是可以解决问题的,但一个项目中会包含很多业务模块,每个业务模块又有很多接口,一个接口又包含很多方法,如果我们要在每个业务方法中都记录方法的耗时,对于程序员而言,会增加很多的工作量。

        而 AOP 就可以做到在不改动这些原始方法的基础上,针对特定的方法进行功能的增强(AOP的作用程序运行期间,在不修改源代码的基础上对已有方法进行增强 ——> 无入侵性:解耦)


二、Spring AOP 快速入门

1、引入AOP依赖

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

2、编写AOP程序

@Slf4j
@Component
@Aspect
public class TimeRecordAspect {
    /**
     * 记录耗时
     */
    @Around("execution(* com.example.books_management_system.controller.*.*(..))")
    public Object timeRecord(ProceedingJoinPoint joinPoint) throws Throwable {
        //记录开始时间
        long star = System.currentTimeMillis();
        //执行目标方法
        Object proceed = joinPoint.proceed();
        //记录结束时间
        long end = System.currentTimeMillis();
        //日志打印耗时
        log.info("耗时时间: " + (end - star) + "ms");
        return proceed;
    }
}

        运行程序,观察日志:

        上面的切点是:com.example.books_management_system.controller.*.*  ,指的是 ...controller包下的所有类和所有方法,都要执行上面的方法(也称为通知)

        对程序进行简单的讲解:

1、@Aspect标识这是一个切面类

2、@Around环绕通知,在目标方法的前后都会被执行。后面的表达式表示对哪些方法进行增强

3、ProceedingJoinPoint.proceed()让原始方法执行

        整个代码划分为三部分

        通过上面的程序,我们可以感受到AOP面向变成的一些优势:

1、代码无侵入:不修改原始的业务方法,就可以对原始的业务方法进行了功能的增强或者是功能的改变。

2、减少了重复代码

3、提高开发效率

4、方便维护


三、Spring AOP 详解

1、Spring AOP的核心概念

(1)切点(Pointcut)

        也称之为 “切入点”,Pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述),告诉程序对哪些方法来进行功能增强

        上面的表达式:execution(* com.example.books_management_system.controller.*.*(..)) ,就是切点表达式

(2)连接点(Join Point)

        满足切点表达式规则的方法,就是连接点。也就是可以被AOP控制的方法。

        以入门程序举例,所有 com.example.books_management_system.controller 路径下的方法,都是连接点。

        

(3)通知(Advice)

        通知就是具体要做的工作,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法),比如上述程序中记录业务方法的耗时时间,就是通知。

        在AOP面向切面编程当中,我们把这部分重复的代码逻辑取出来单独定义,这部分代码就是通知的内容。

(4)切面(Aspect)

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

        通过切面就能够描述当前AOP程序需要针对于哪些方法,在什么时候执行什么样的操作

        切面所在的类,我们一般称为切面类(被@Aspect注解表示的类)。

(5)总结

        切点就是一规则,连接点就是符合这个规则的对象,通知就是对象做的事情,切面就是符合规则的所有对象做事

        举个栗子:报名蓝桥杯的学生在 2024-04-13 参比赛。

切点:报名蓝桥杯的学生。

连接点:这些学生具体的每一个人。

通知:在 2024-04-13 参加比赛。

切面:报名蓝桥杯的学生在 2024-04-13 参比赛。

2、通知类型

        上面我们讲了什么是通知,接下来学习通知的类型。@Around 就是其中一种通知类型,表示环绕通知。Spring 中 AOP 的通知类型有以下几种:

@Around环绕通知,此注解标注的通知方法在目标方法前、后都被执行

@Before前置通知,此注解标注的通知方法在目标方法前被执行

@After后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行

@AfterReturning返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行

@AfterThrowing异常后通知,此注解标注的通知方法发生异常后执行

        接下来测试这些通知类型,代码如下:

@Slf4j
@Aspect
@Component
public class AspectDemo {
    //前置通知
    @Before("execution(* com.example.springaop.controller.*.*(..))")
    public void doBefore() {
        log.info("do before");
    }
    //后置通知
    @After("execution(* com.example.springaop.controller.*.*(..))")
    public void doAfter() {
        log.info("do after");
    }
    //添加环绕通知
    @Around("execution(* com.example.springaop.controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("do around before");
        Object result = joinPoint.proceed();
        log.info("do around after");
        return result;
    }
    //返回后通知
    @AfterReturning("execution(* com.example.springaop.controller.*.*(..))")
    public void doAfterReturning() {
        log.info("do after returning");
    }
    //抛出异常后通知
    @AfterThrowing("execution(* com.example.springaop.controller.*.*(..))")
    public void doAfterThrowing() {
        log.info("do after throwing");
    }
}

        测试接口:

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1() {
        log.info("执行t1方法...");
        return "t1";
    }
    @RequestMapping("/t2")
    public Boolean t2() {
        int a = 10 / 0;
        return true;
    }
}

(1)正常运行的情况

        接口:http://127.0.0.1:8080/test/t1 ,测试结果如下:

        执行的顺序是 Around Before -> Before -> AfterReturning -> After -> Around After,如图:

        程序正常运行的情况下,AfterThrowing 标识的通知方法不会执行。

(2)运行失败的情况

        接口:http://127.0.0.1:8080/test/t2 ,测试结果如下:

        执行顺序:Around Before -> Before -> AfterThrowing-> After,如图:

        可以看到,因为有异常,所以会执行 AfterThrowing,没有执行 AfterReturning,改成了 AfterThrowing而 After 无论是否有异常都会执行因为有异常,所以执行完目标方法后,就不会执行 AroundAfter 了

(3)@Around 通知类型

        其中单独测试一下 @Around 通知类型,有返回Object类型 和 没有返回Object类型的区别,代码如下:

        无返回Object类型:

    @Around("execution(* com.example.springaop.controller.*.*(..))")
    public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("do around before");
        log.info("do around after");
    }

        又返回Object类型:

    @Around("execution(* com.example.springaop.controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("do around before");
        Object result = joinPoint.proceed();
        log.info("do around after");
        return result;
    }

        无返回类型 和 有返回类型 的区别图:

        所以,@Around 环绕通知方法的返回值,必须指定为 Object 类型。

(4)注意事项

1、@Around 环绕通知需要调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行。

2、@Around 环绕通知方法的返回值,必须指定为Object 类型,来接收原始方法的返回值,否则原始方法执行完毕,使获取不到返回值的。

3、一个切面类可以有多个切点。


3、@PointCut

        上面代码存在一个问题,就是存在大量重复的切点表达式:

execution(* com.example.springaop.controller.*.*(..)),Spring 提供了 @PointCut 注解,把公共的切点表达式提取出来,需要用到时引入该切入点表达式即可

        上面代码就可以修改为:

@Slf4j
@Aspect
@Component
public class AspectDemo {
    @Pointcut("execution(* com.example.springaop.controller.*.*(..))")
    private void pt(){}
    //public void pt(){} 其他类要使用pt()时,使用public修饰
    //前置通知
    @Before("pt()")
    public void doBefore() {
        log.info("do before");
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        log.info("do after");
    }
    //添加环绕通知
    @Around("pt()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("do around before");
        Object result = joinPoint.proceed();
        log.info("do around after");
        return result;
    }
    //返回后通知
    @AfterReturning("pt()")
    public void doAfterReturning() {
        log.info("do after returning");
    }
    //抛出异常后通知
    @AfterThrowing("pt()")
    public void doAfterThrowing() {
        log.info("do after throwing");
    }
}

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

        当其他切面类使用上面这个PointCut时,代码如下:

@Slf4j
@Aspect
@Component
public class AspectDemo2 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo2 -> Before ⽅法");
    }
}

        测试接口:http://127.0.0.1:8080/test/t1,日志如下:

 

4、切面优先级 @Order

        当我们在一个项目中,定义了多个切面类时,并且这些切面类的多个切入点都匹配到了同一个目标方法。当目标方法运行的时候,这些切面类中的通知方法都会执行,那么这几个通知方法的执行顺序是什么样的呢?

        那么不同类的 通知 运行的顺序是怎么样的呢?现在切面类有下面几个:

@Slf4j
@Aspect
@Component
public class AspectDemo2 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo2 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo2 -> After ⽅法");
    }
}

@Slf4j
@Aspect
@Component
public class AspectDemo3 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo3 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo3 -> After ⽅法");
    }
}

@Slf4j
@Aspect
@Component
public class AspectDemo4 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo4 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo4 -> After ⽅法");
    }
}

        测试顺序如下:

        可以看到 默认给我们的排序是按切面类名大小进行排序的。存在多个切面类时,默认按照切面类的类名字母排序

@Before 通知字母排名靠前的先执行

@After 通知字母排名靠前的后执行

        那如果我们不想按这个顺序,按照自定义顺序行不行?当然可以了,下面是@Order注解的使用(@Order注解 的括号里面放数字)

@Slf4j
@Aspect
@Order(3)
@Component
public class AspectDemo2 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo2 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo2 -> After ⽅法");
    }
}

@Slf4j
@Aspect
@Order(2)
@Component
public class AspectDemo3 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo3 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo3 -> After ⽅法");
    }
}

@Slf4j
@Aspect
@Order(1)
@Component
public class AspectDemo4 {
    @Before("com.example.springaop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("执⾏ AspectDemo4 -> Before ⽅法");
    }

    @After("com.example.springaop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("执⾏ AspectDemo4 -> After ⽅法");
    }
}

        运行观察日志,如图:

        

        通过上述程序的运行结果,得出结论,@Order 注解标识的切面类,执行顺序如下:

@Before 通知数字小 的 先执行

@After 通知数字大 的先执行

        @Order 控制切面的 优先级,先执行优先级较高的切面,再执行优先级较低的切面,最终执行目标方法。如图:

5、切点表达式

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

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

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

(1)execution 表达式

        execution() 是最常用的切点表达式,用来匹配方法,语法为:

execution( <访问修饰符>  <返回类型>  <包名.类名.方法(方法参数)> <异常> )

上面的切点表达式意思是:任意的返回类型,在....controller包下的任意类方法(任意方法参数都行)

        其中:访问修饰符和异常可以省略。

        切点表达式支持通配符:

1、* 匹配任意字符,只匹配一个元素(返回类型,包,类名,方法或者方法参数)

a、包名使用 * 表示任意包(一层包使用一个 *)。

b、类名使用 * 表示任意类。

c、返回值使用 * 表示任意返回值类型。

d、方法名使用 * 表示任意方法。

e、参数使用 * 表示一个任意类型的参数。

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

a、使用 .. 配置包名,标识此包以及此包下的所有子包。

b、可以使用 .. 配置参数,任意个任意类型的参数。

切点表达式

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.*())

 匹配TestController 下的所有方法:

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

匹配controller包下所有的类的所有方法:

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

匹配所有包下⾯的TestController:

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

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

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

(2)@annotation

        execution 表达式更适用有规则的,如果我们要匹配多个无规则的方法呢?比如:TestController 中的 t1() 和 UserController 中的 u1() 这两个方法。

        这个时候我们使用 execution 这种 切点表达式来描述就很不方便了。

        这时我们就可以借助自定义注解的方式以及另一种切点表达式 @annotation 来描述这一类的切点

        实现步骤:

1编写自定义注解

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

3在连接点的方法上添加自定义注解

准备测试代码:

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1() {
        log.info("执行t1方法...");
        return "t1";
    }
    @RequestMapping("/t2")
    public Boolean t2() {
        log.info("执行t2方法...");
        return true;
    }
}

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {

    @RequestMapping("/u1")
    public String u1() {
        log.info("执行u1方法...");
        return "u1";
    }

    @RequestMapping("/u2")
    public String u2() {
        log.info("执行u2方法...");
        return "u2";
    }
}
① 自定义注解 @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 {
}

        代码的简单说明:

1@Target 标识了 Annotation 所修饰的对象范围,即该注解可以用在什么地方

  常用取值:

        ElementType.TYPE用于描述类、接口(包括注解类型) 或 enum声明
        ElementType.METHOD描述方法

        ElementType.PARAMETER描述参数

        ElementType.TYPE_USE可以标注任意类型
2@Retention 指 Annotation 被保留的时间长短,标明注解的生命周期

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

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

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

② 切面类

        使用 @annotation 切点表达式定义切点,只对 @MyAspect生效,代码如下:

@Slf4j
@Component
@Aspect
public class MyAspectDemo {
    @Around("@annotation(com.example.springaop.config.MyAspect)")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        log.info("do around before");
        Object o = null;
        try {
            o = joinPoint.proceed();
        } catch (Throwable e) {
            log.error("发生异常, e:" + e);
        }
        log.info("do around after");
        return o;
    }
}

        上面的通知类型使用 @annotation注解后,注解括号里的 MyAspect,表示谁使用 @MyAspect注解,就会执行下面代码

③ 添加自定义注解

        在 TestController 中的 t1() 和 UserController 中的 u1 这两个方法上添加自定义注解 @MyAspect,其他方法不添加。(已把之前的AspectDemo的@Comment注解给注释掉了)

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    @MyAspect
    @RequestMapping("/u1")
    public String u1() {
        log.info("执行u1方法...");
        return "u1";
    }
}
@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @MyAspect
    @RequestMapping("/t1")
    public String t1() {
        log.info("执行t1方法...");
        return "t1";
    }
}

        运行程序,测试接口:

        
127.0.0.1:8080/test/t1
 

        

        控制台日志如下:

        127.0.0.1:8080/user/u1

        可以看到,通过自定义注解的方式,就可以匹配多个无规则的方法。

6、Spring AOP 的实现方式(常见面试题)

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

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

(3)基于 Spring API(通过 xml 配置的方式,自从SpringBoot 广泛使用之后,这种方法几乎看不到了)

(4)基于代理来实现(更加久远的一种实现方式,写法笨重,不建议使用)

参考:面试官:谈谈你对IOC和AOP的理解及AOP四种实现方式[通俗易懂]-腾讯云开发者社区-腾讯云 (tencent.com)

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

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

相关文章

SQL labs靶场-SQL注入入门

靶场及环境配置参考 一&#xff0c;工具准备。 推荐下载火狐浏览器&#xff0c;并下载harkbar插件&#xff08;v2&#xff09;版本。hackbar使用教程在此不做过多描述。 补充&#xff1a;url栏内部信息会进行url编码。 二&#xff0c;SQL注入-less1。 1&#xff0c;判断传参…

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之一 [ 如何寻找大型数据集 ] / [ 构建词元分析器 ] 的简单整理

AGI 之 【Hugging Face】 的【从零训练Transformer模型】之一 [ 如何寻找大型数据集 ] / [ 构建词元分析器 ] 的简单整理 目录 AGI 之 【Hugging Face】 的【从零训练Transformer模型】之一 [ 如何寻找大型数据集 ] / [ 构建词元分析器 ] 的简单整理 一、简单介绍 二、Transf…

tslib 库-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

tslib 库 tslib 简介 tslib 库&#xff0c;这是 Linux 系统下&#xff0c;专门为触摸屏开发的应用层函数库&#xff0c;开源 功能与作用&#xff1a;作为触摸屏驱动和应用层之间的适配层&#xff0c;封装了读取和解析触摸屏数据的复杂过程&#xff0c;提供API接口 数据处理&…

工具(linux)

Yum 软件包管理器 介绍 yum Yum 是一个在 Red Hat 和 CentOS 等 Linux 发行版中常用的软件包管理器&#xff0c;它可以方便地进行软件包的安装、更新和删除。 安装软件包 使用 yum install 命令可以安装指定的软件包&#xff0c;例如&#xff1a; yum install package_nam…

《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

华为OD2024D卷机试题汇总,含D量50%+,按算法分类刷题,事半功倍

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、链表5、栈6、滑动窗口7、二叉树8、并查集9、矩阵 三、算法1、基础算法① 贪心思维② 二分查…

Graph Guided Transformer论文精读

摘要 抛出问题&#xff1a;基于深度学习的框架在将平衡局部空间结构信息的时候对于区域信息的特征建模能力会下降&#xff01; 紧接着提出一个GT&#xff08;Graph–guided transformer&#xff09;模型&#xff0c;整个网络的框架为&#xff1a;首先使用卷积块获取像素级别的…

开源项目:推动经济与科技发展的新引擎

文章目录 每日一句正能量前言开源项目的发展趋势开源项目的发展趋势开源社区的活跃度开源项目在技术创新中的作用结论 参与开源的经验分享如何选择开源项目如何贡献代码如何与开源社区合作个人品牌与职业发展贡献与成就感 开源项目的挑战维护难度版权问题社区管理技术债务安全问…

理解进程status的二进制位表示及进程等待(是什么,为什么,怎么办)

信号编号&#xff1a;低7位 状态编号&#xff1a;次低8位 1.子进程退出后会变为僵尸进程&#xff0c;将退出结果写入自身的task_struct结构体中 2.wait/waitpid是一个系统调用->OS可以读取子进程的task_struct 1.为什么要进行进程等待&#xff1f; 1.将子进程&#xff…

Vatee万腾平台:智慧金融的创新引擎,驱动金融服务升级

在数字化浪潮席卷全球的今天&#xff0c;金融行业正经历着前所未有的变革与升级。Vatee万腾平台&#xff0c;作为智慧金融领域的创新先锋&#xff0c;正以其独特的技术优势、前瞻的战略布局和卓越的服务能力&#xff0c;成为推动金融服务升级的重要引擎。 智慧金融&#xff1a;…

上海联联充电互联互通充电系统嘎嘎快充云快充旭电通池续特来电等等免费对接

1、定时将我方订单信息推送到联联监管 2、实现联联充电Pro使用我方设备充电 3、使用我方小程序开启快电、特来电、星星充电、新电途、石化养车等等

资源调度的艺术:大规模爬虫管理的优化策略

摘要 本文深入探讨了在处理大规模数据抓取项目时&#xff0c;如何通过优化资源调度策略来提升爬虫管理的效率与稳定性。从技术选型到策略实施&#xff0c;揭示了优化的核心技巧&#xff0c;助力企业与开发者高效驾驭大数据采集的挑战。 正文 在互联网信息爆炸的时代&#xf…

iOS ------ weak的基本原理

1.weak的基本概念 weak弱引用&#xff0c;所引用的对象的引用计数不会加一&#xff0c;引用对象被释放的时候会自动设置为nil多用于解决对象间的相互引用造成内存泄露的循环引用的问题 2.实现原理 Person *object [[Person alloc] init]; id __weak objc object;Runtime维…

《程序猿学会 Vue · 基础与实战篇》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

Linux环境下MongoDB安装与主从集群,高可用集群配置教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

VMware 安装完,设备管理器中没有虚拟网卡(vmnet0、wmnet1、vmnet8) / 虚拟网络编辑器中没有桥接模式

问题&#xff1a;VMware 安装完&#xff0c;设备管理器中没有虚拟网卡(vmnet0、wmnet1、vmnet8) / 虚拟网络编辑器中没有桥接模式 1、确认 Device Install Service 和 Device Setup Manager 没有被禁用 Device Install Service 和 Device Setup Manager是 Windows 操作系统中…

C# 2.数组

1.定义整形数组&#xff0c;指定数组长度 string[] s1 new string[3]; byte[] bs new byte[3]; 数组的索引值从0开始&#xff0c;3个元素索引值为0&#xff0c;1,2…… 2.添加元素&#xff1a;数组【索引值】值 i1[0] 3;// 给索引值位0位置添加一个3 3 i1[0] 取出指定位置的…

【LeetCode:3098. 求出所有子序列的能量和 + 记忆化缓存】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

JUC-synchorized与锁原理、锁的升级与膨胀

syn-ed 是一个可重入、不公平的重量级锁&#xff1b;synchronized使用对象锁保证了临界区代码的原子性&#xff0c;无论使用synchorized锁的是代码块还是方法&#xff0c;其本质都是锁住一个对象。 同步代码块&#xff0c;锁住的是括号里的对象同步方法 普通方法&#xff0c;…

手写文件系统一条龙篇

哈喽&#xff0c;我是子牙老师。这篇文章聊聊文件系统是怎么写出来的&#xff0c;看完&#xff0c;你就可以自己去写文件系统了。本文以Linux的EXT系列文件系统为例进行讲解&#xff0c;理解了&#xff0c;其他的文件系统你就可以自行研究了&#xff0c;差不多的东西 理解了本…