Spring系统学习 - AOP之基于注解的AOP和XML的AOP

news2025/1/11 8:12:10

上一篇我们围绕了AOP中代理模式的使用,这篇我们将主要围绕AOP的相关术语介绍,以及重点围绕基于注解的AOP进行相关知识的概述和使用说明。

AOP的相关术语

  1. 切面(Aspect):切面是一个模块化的横切关注点,它包含了一组通知和切点。通常,切面用于定义横切关注点的行为,如日志记录、事务管理等。
    在这里插入图片描述

  2. 通知(Advice):通知是切面在特定切点上执行的动作。在 Spring AOP 中,有以下几种类型的通知:

    • 前置通知(Before Advice):在目标方法执行之前执行。
    • 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否抛出异常。
    • 返回通知(After Returning Advice):在目标方法成功执行并返回结果后执行。
    • 异常通知(After Throwing Advice):在目标方法抛出异常后执行。
    • 环绕通知(Around Advice):在目标方法执行前后都执行,并可以控制目标方法的执行。
      在这里插入图片描述
  3. 切点(Pointcut):切点是指在应用程序中选择连接点的表达式。它定义了哪些方法或类应该被通知所影响。切点使用表达式语言来匹配连接点。

  4. 连接点(Join Point):连接点是在应用程序执行过程中可以插入切面的点。它可以是方法调用、方法执行、异常抛出等。
    在这里插入图片描述

  5. 引入(Introduction):引入允许向现有的类添加新的方法或属性。它允许在不修改现有类的情况下,向类添加新的功能。

  6. 目标对象(Target Object):目标对象是被通知的对象,它包含了切面所要应用的方法。

  7. 代理(Proxy):代理是一个对象,它包装了目标对象,并拦截对目标对象的访问。代理可以在目标对象的方法执行前后添加额外的逻辑。

  8. 织入(Weaving):织入是将切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、类加载时或运行时进行。(可以称之为横切关注点)

在这里插入图片描述
这些术语的作用:

  1. 提供统一的概念和术语:术语提供了一套统一的概念和术语,使开发人员能够更好地理解和沟通。通过使用共同的术语,可以减少误解和混淆,提高团队协作效率。
  2. 定义和描述 AOP 的各个组成部分:术语用于定义和描述 AOP 的各个组成部分,如切面、通知、切点等。它们提供了一种标准的方式来描述和理解 AOP 的概念和实现。
  3. 指导开发人员理解和使用 Spring AOP 框架:术语帮助开发人员理解和使用 Spring AOP 框架。通过熟悉和理解这些术语,开发人员可以更好地使用 Spring AOP 提供的功能和特性,实现横切关注点的处理。
  4. 促进知识共享和学习:术语作为一种共享的语言,促进了知识的共享和学习。开发人员可以通过使用相同的术语来交流和分享经验,从而加深对 Spring AOP 的理解和应用。

基于注解的AOP

基于注解的 AOP 是一种使用注解来定义切面和通知的方式。在传统的基于 XML 配置的 AOP 中,切面和通知的定义通常是通过 XML 配置文件来完成的,而基于注解的 AOP 则使用注解来实现这些定义,使得配置更加简洁和直观。

在这里插入图片描述

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。

  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。

  • AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

常见的注解:

  • @Aspect:用于定义切面类,标识该类为切面。
  • @Before:用于定义前置通知,在目标方法执行之前执行。
  • @After:用于定义后置通知,在目标方法执行之后执行,无论目标方法是否抛出异常。
  • @AfterReturning:用于定义返回通知,在目标方法成功执行并返回结果后执行。
  • @AfterThrowing:用于定义异常通知,在目标方法抛出异常后执行。
  • @Around:用于定义环绕通知,在目标方法执行前后都执行,并可以控制目标方法的执行。

案例

导入相关依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SSM</artifactId>
        <groupId>com.miaow</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Spring-AOP</artifactId>
    <description>Spring的AOP学习</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.20</version>
        </dependency>

<!--        引入AOP,实际上就是spring-aspect会帮我传递过来aspectjweaver-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.8</version>
        </dependency>
    </dependencies>

</project>

创建接口实现注解横切

public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);

}
@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部,result:"+result);
        return result;
    }
}
@Component
@Aspect //将当前组件标识为切面
public class LoggerAspect {

    @Pointcut("execution(* com.miaow.aspect.CalculatorImpl.*(..))")
    public void pointCut(){}

    //@Before("execution(public int com.miaow.aspect.CalculatorImpl.add(int, int))")
    //@Before("execution(* com.miaow.aspect.CalculatorImpl.*(..))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",执行完毕");
    }

    /**
     * 在返回通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterReturning注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
    }

    /**
     * 在异常通知中若要获取目标对象方法的异常
     * 只需要通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     */
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

    //使用了环绕通知就不需要使用上述四种通知,如果选择上面的四种通知,那么就不需要选择下面的环绕通知
    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象方法的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-->前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->异常通知");
        } finally {
            System.out.println("环绕通知-->后置通知");
        }
        return result;
    }

}
/**
 * 这段代码定义了一个名为 ValidateAspect 的 Aspect,它使用 @Order 注解将其优先级设置为 1,并使用 @Before 注解定义一个前置通知方法。
 */
@Component
@Aspect
@Order(1)  //order=1值越小,优先级越高
public class ValidateAspect {

    //@Before("execution(* com.miaow.aspect.CalculatorImpl.*(..))")
    @Before("com.miaow.aspect.LoggerAspect.pointCut()")
    public void beforeMethod(){
        System.out.println("ValidateAspect-->前置通知");
    }

}

在Spring 配置文件中添加下述代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--
     基于注解的AOP的实现:
     1、将目标对象和切面交给IOC容器管理(注解+扫描)
     2、开启AspectJ的自动代理,为目标对象自动生成代理
     3、将切面类通过注解@Aspect标识
 -->

<!--    扫码component文件-->
    <context:component-scan base-package="com.miaow.aspect"></context:component-scan>

<!--    启用基于注解AOP,否则实现不了-->
    <aop:aspectj-autoproxy />
</beans>

测试类:

    @Test
    public void test2(){
        //先获取IOC
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
        //当我们使用AOP之后无法直接获取对象,只能通过动态代理的获取
        com.miaow.aspect.Calculator calculator =  context.getBean(com.miaow.aspect.Calculator.class);
//        calculator.add(1,2);
//        calculator.sub(1,2);
//        calculator.mul(1,2);
        calculator.div(1,2);
    }

运行结果:

ValidateAspect-->前置通知
环绕通知-->前置通知
LoggerAspect,方法:div,参数:[1, 2]
方法内部,result:0
LoggerAspect,方法:div,结果:0
LoggerAspect,方法:div,执行完毕
环绕通知-->返回通知
环绕通知-->后置通知

补充说明AOP中的切面优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

在这里插入图片描述

在基于注解的 AOP 中,切面的优先级可以通过以下方式进行控制:

  • @Order 注解:可以在切面类上使用 @Order 注解来指定切面的优先级。@Order 注解的值越小,优先级越高。如果多个切面都使用了 @Order 注解,则优先级较高的切面将先于优先级较低的切面执行。
@Aspect
@Order(1)
public class MyAspect1 {
    // ...
}

@Aspect
@Order(2)
public class MyAspect2 {
    // ...
}
  • 实现 Ordered 接口:可以让切面类实现 Ordered 接口,并实现其中的 getOrder() 方法来指定切面的优先级。getOrder() 方法返回的值越小,优先级越高。
@Aspect
public class MyAspect implements Ordered {
    // ...
    
    @Override
    public int getOrder() {
        return 1;
    }
}
  • @Order 和 Ordered 接口的组合使用:可以同时使用 @Order 注解和实现 Ordered 接口的方式来控制切面的优先级。当切面类既使用了 @Order 注解,又实现了 Ordered 接口时,@Order 注解的优先级高于 Ordered 接口的 getOrder() 方法返回值。
@Aspect
@Order(1)
public class MyAspect implements Ordered {
    // ...
    
    @Override
    public int getOrder() {
        return 2;
    }
}

需要注意的是,切面的优先级仅在多个切面同时应用于同一个连接点时才会起作用。如果切面应用于不同的连接点,优先级的设置将不会生效。

了解基于XML的AOP操作

在基于 XML 的 Spring AOP 中,切面和通知的定义通常是通过 XML 配置文件来完成的。以下是基于 XML 的 Spring AOP 的配置步骤:

  1. 定义切面类:创建一个切面类,其中包含了切面的逻辑和通知的定义。
  2. 创建 XML 配置文件:创建一个 XML 配置文件,用于定义切面和通知的关系。
  3. 配置切面和通知:在 XML 配置文件中,使用 <aop:config> 元素来配置切面和通知。
  4. 定义切点:使用 <aop:pointcut> 元素来定义切点,切点用于匹配连接点。
  5. 配置通知:使用 <aop:advisor> 元素来配置通知,将切面和切点关联起来。
  6. 引入其他配置:根据需要,可以在 XML 配置文件中引入其他的配置,如引入目标对象、引入代理等。

在这里插入图片描述

基于XML的AOP操作展示

public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);

}
@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部,result:"+result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部,result:"+result);
        return result;
    }
}
@Component
@Aspect //将当前组件标识为切面
public class LoggerAspect {

    public void pointCut(){}

    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

    public void afterAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",执行完毕");
    }

    /**
     * 在返回通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterReturning注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     */
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
    }

    /**
     * 在异常通知中若要获取目标对象方法的异常
     * 只需要通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     */
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-->前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->异常通知");
        } finally {
            System.out.println("环绕通知-->后置通知");
        }
        return result;
    }

}
@Component
public class ValidateAspect {
    public void beforeMethod(){
        System.out.println("ValidateAspect-->前置通知");
    }

}

XML文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:c="http://www.springframework.org/schema/c"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.miaow.xml"></context:component-scan>

    <aop:config >
<!--        将IOC容器的bean设置为切面 @Component 自动将我们标记的类设置为类名小字母开头-->
<!--        设置一个共同的切入点表达式-->
        <aop:pointcut id="pointCut" expression="execution(* com.miaow.xml.CalculatorImpl.*(..))"/>
        <aop:aspect ref="loggerAspect">
            <aop:before method="beforeAdviceMethod" pointcut-ref="pointCut"></aop:before>
            <aop:after method="afterAdviceMethod" pointcut-ref="pointCut"></aop:after>
            <aop:after-returning method="afterReturningAdviceMethod" returning="result" pointcut-ref="pointCut"></aop:after-returning>
            <aop:after-throwing method="afterThrowingAdviceMethod" throwing="ex" pointcut-ref="pointCut"></aop:after-throwing>
            <aop:around method="aroundAdviceMethod" pointcut-ref="pointCut"></aop:around>

        </aop:aspect>

        <!--    设置优先级-->
        <aop:aspect ref="validateAspect" order="1">
            <aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before>
        </aop:aspect>

    </aop:config>
</beans>

测试类

public class AOPByXMLTest {

    @Test
    public void testAop(){
        ApplicationContext context = new ClassPathXmlApplicationContext("Spring-aop-xml.xml");
        Calculator calculator = (Calculator) context.getBean(Calculator.class);
        calculator.add(1,2);
    }
}
ValidateAspect-->前置通知
LoggerAspect,方法:add,参数:[1, 2]
环绕通知-->前置通知
方法内部,result:3
环绕通知-->返回通知
环绕通知-->后置通知
LoggerAspect,方法:add,结果:3
LoggerAspect,方法:add,执行完毕

通过 aop:config 元素配置了切面和通知,使用 aop:pointcut 元素定义了切点,使用 aop:before、aop:after、aop:after-returning、aop:after-throwing、aop:around 元素配置了不同类型的通知。

通过基于 XML 的配置,可以灵活地定义和配置切面和通知,实现对目标对象的横切关注点的处理。

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

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

相关文章

【关于车载测试的基础知识的认知详解】

目录 一、目前车企的趋势 1. 电动化&#xff1a; 2. 自动驾驶技术&#xff1a; 3. 车联网&#xff08;Connected Cars&#xff09;&#xff1a; 4. 智能化和数字化&#xff1a; 5. 安全性&#xff1a; 6. 轻量化&#xff1a; 7. 个性化和定制化&#xff1a; 8. 供应链…

C基础day7

一、思维导图 二、课后练习 1、提示并输入一个字符串&#xff0c;统计该字符串中字母、数字、空格以及其他字符的个数 #include<myhead.h> #define M 20 int main(int argc, const char *argv[]) {int sum_a0,sum_b0,sum_c0,sum_d0;char str[M];printf("please en…

Vagrant配合VirtualBox搭建虚拟机

目录 前言一、软件下载及安装1.下载2.安装扩展&#xff1a; 二、创建一个虚拟机1.Vagrant官方镜像仓库 三、使用远程工具连接虚拟机1.修改相关配置文件 四、虚拟机克隆及使用1.通用配置2.简单搭建一个java环境3.克隆虚拟机1.重命名虚拟机&#xff08;可选&#xff09;2.打包指定…

阿里发布大模型发布图结构长文本处理智能体,超越GPT-4-128k

随着大语言模型的发展&#xff0c;处理长文本的能力成为了一个重要挑战。虽然有许多方法试图解决这个问题&#xff0c;但都存在不同程度的局限性。最近&#xff0c;阿里巴巴的研究团队提出了一个名为GraphReader的新方法&#xff0c;通过将长文本组织成图结构&#xff0c;并利用…

在FPGA程序中Handshake(握手)和Register(寄存器)区别

在FPGA程序中&#xff0c;Handshake&#xff08;握手&#xff09;和Register&#xff08;寄存器&#xff09;是两种不同的通信和数据传输机制。它们各有特点和适用场景。以下是它们的区别和应用场景的详细解释&#xff1a; Register&#xff08;寄存器&#xff09; 特点&#…

入门PHP就来我这(高级)19 ~ 捕获sql错误

有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 接着上篇我们来看下sql错误的捕获模式。 1 PDO中捕获SQL语句中的错误 在PDO中有3种方法可以捕…

产品经理/项目经理管理项目使用最多的12款项目软件对比

盘点不同行业、项目类型的下的12款主流的项目管理软件&#xff1a;PingCode、Worktile、Teambition、TAPD、广联达、Asana、Basecamp、Jira、Microsoft Project、ClickUp、Redmine、Trello。 在这个项目管理工具层出不穷的时代&#xff0c;选择一个合适的软件似乎成了一个令许多…

博客标题:C++中的继承:构建面向对象的基石

目录 ​编辑 引言 继承的基本形式 示例1&#xff1a;基本继承 继承的类型 示例2&#xff1a;不同类型的继承 多重继承 示例3&#xff1a;多重继承 继承与多态性 示例4&#xff1a;继承与多态 结论 结尾 引言 在面向对象编程&#xff08;OOP&#xff09;中&#xff…

可以添加todo清单桌面小组件的便签哪个好?

在我们快节奏的生活中&#xff0c;有效的时间管理和任务追踪是必不可少的。为了实现这一目标&#xff0c;许多人选择使用桌面便签&#xff0c;尤其是那些具有Todo清单桌面小组件的便签。但是&#xff0c;面对市场上众多选择&#xff0c;可以添加todo清单桌面小组件的便签哪个好…

企业级网关设计

tips&#xff1a;本文完全来源于卢泽龙&#xff01;&#xff01;&#xff01; 一、Gateway概述 1.1设计目标 1.2gateway基本功能 中文文档参考&#xff1a;https://cloud.tencent.com/developer/article/1403887?from15425 三大核心&#xff1a; 二、引入依赖和yaml配置…

14-52 剑和诗人26 - RAG 和 VectorDB 简介

检索增强生成 (RAG) 和 VectorDB 是自然语言处理 (NLP) 中的两个重要概念&#xff0c;它们正在突破 AI 系统所能实现的界限。 在这篇博文中&#xff0c;我将深入探讨 RAG&#xff0c;探索其工作原理、应用、优势和局限性。 我们还将研究 VectorDB&#xff0c;这是一种专用于向…

【动态规划Ⅵ】背包问题 /// 组合问题

背包问题 什么是背包问题0-1背包问题分数背包完全背包问题重复背包问题 背包问题例题416. 分割等和子集474. 一和零 完全平方数279. 完全平方数322. 零钱兑换 排列与组合组合&#xff0c;无重复&#xff1a;518. 零钱兑换 II排列&#xff0c;可重复&#xff1a;377. 组合总和 Ⅳ…

Commons-Collections篇-CC7链

前言 和CC5反序列化链相似&#xff0c;CC7也是后半条LazyMap执行命令链不变&#xff0c;但是中间过程通过AbstractMap.equals()触发LazyMap.get()方法 环境 我们可以接着使用之前已经搭建好的环境&#xff0c;具体过程可以看CC1分析文章的环境安装部分 Commons-Collections篇…

【Java 的四大引用详解】

首先分别介绍一下这几种引用 强引用&#xff1a; 只要能通过GC ROOT根对象引用链找到就不会被垃圾回收器回收&#xff0c;当所有的GC Root都不通过强引用引用该对象时&#xff0c;才能被垃圾回收器回收。 软引用&#xff08;SoftReference&#xff09;&#xff1a; 当只有软引…

262个地级市-市场潜力指数(do文件+原始文件)

全国262个地级市-市场潜力指数&#xff08;市场潜力计算方法代码数据&#xff09;_市场潜力数据分析资源-CSDN文库 市场潜力指数&#xff1a;洞察未来发展的指南针 市场潜力指数是一个综合性的评估工具&#xff0c;它通过深入分析市场需求、竞争环境、政策支持和技术创新等多个…

LLM应用构建前的非结构化数据处理(一)标准化处理认识数据

1.学习内容 本节次学习内容来自于吴恩达老师的Preprocessing Unstructured Data for LLM Applications课程&#xff0c;因涉及到非结构化数据的相关处理&#xff0c;遂做学习整理。 2.相关环境准备 2.1 建议python版本在3.9版本以上 chromadb0.4.22 langchain0.1.5 langcha…

Redis-Jedis连接池\RedisTemplate\StringRedisTemplate

Redis-Jedis连接池\RedisTemplate\StringRedisTemplate 1. Jedis连接池1.1 通过工具类1.1.1 连接池&#xff1a;JedisConnectionFactory&#xff1a;1.1.2 test&#xff1a;&#xff08;代码其实只有连接池那里改变了&#xff09; 2. SpringDataRedis&#xff08;lettuce&#…

洛谷 数学进制 7.9

P1100 高低位交换 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码一 #include<bits/stdc.h> using namespace std; typedef long long ll; #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)const ll N1e510; char a[N];int main() {IOS;ll a;int b[32]…

【分布式系统】ceph部署(命令+截图巨详细版)

目录 一.存储概述 1.单机存储设备 2.单机存储的问题 3.商业存储 4.分布式存储​编辑 4.1.什么是分布式存储 4.2.分布式存储的类型 二.ceph概述 1.ceph优点 2.ceph架构 3.ceph核心组件 4.OSD存储后端 5.ceph数据存储过程 6.ceph版本发行生命周期 7.ceph集群部署 …

用ce修改植物大战僵尸杂交版银币

第一步打开游戏 用ce打开图中进程 第二步 输入你原始银币 点首次搜索 第三步 找到这个地址 把地址拖下来 第四步 双击直接修改下面数值即可 金币 钻石 都和这个方法一样 不一样的是首次搜索可能会有很多地址 我们改变游戏里面的值 然后再次搜索游戏被改变的值即可准确找到地址