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

上一篇我们围绕了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 的配置,可以灵活地定义和配置切面和通知,实现对目标对象的横切关注点的处理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mybatis-plus参数绑定异常

前言 最近要搞个发票保存的需求&#xff0c;当发票数据有id时说明是发票已经保存只需更新发票数据即可&#xff0c;没有id时说明没有发票数据需要新增发票&#xff1b;于是将原有的发票提交接口改造了下&#xff0c;将调用mybatis-plus的save方法改为saveOrUpdate方法&#xff…

C++:多态(继承)

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;多态》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 文章目录 :maple_leaf:多态的概念:maple_leaf:继承中的多态1.:leaves:虚函数表 :…

更亮更好听的户外耳机,下班之后畅快运动,哈氪聆光体验

在当今市场上&#xff0c;蓝牙耳机种类繁多&#xff0c;现在正值酷热的夏季&#xff0c;有必要准备一副适合户外活动的蓝牙耳机&#xff0c;对此&#xff0c;我觉得气传导耳机更适合在户外锻炼或散步时使用。这种耳机设计通常为后挂式&#xff0c;不仅佩戴舒适&#xff0c;而且…

宝塔面板运行Admin.net框架

准备 宝塔安装 .netcore安装 Admin.net框架发布 宝塔面板设置 完结撒花 1.准备 服务器/虚拟机一台 系统Windows server / Ubuntu20.04&#xff08;本贴使用的是Ubuntu20.04版本系统&#xff09; Admin.net开发框架 先安装好服务器系统&#xff0c;这里就不做安装过程描述了&…

安装nodejs | npm报错

nodejs安装步骤: 官网&#xff1a;https://nodejs.org/en/ 在官网下载nodejs: 双击下载下来的msi安装包&#xff0c;一直点next&#xff0c;我选的安装目录是默认的: 测试是否安装成功&#xff1a; 输入cmd打开命令提示符&#xff0c;输入node -v可以看到版本&#xff0c;说…

“郑商企航”暑期社会实践赴美丽美艳直播基地开展调研

马常旭文化传媒网讯&#xff08;记者张明辉报道&#xff09;导读&#xff1a;2024 年 7 月 3 日&#xff0c;商学院暑期社会实践团“郑商企航”在河南省郑州市新密市岳村镇美丽美艳直播基地&#xff0c;展开了一场意义非凡的考察活动&#xff0c;团队成员深度调研了直播基地的产…

DMA方式的知识点笔记

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 目录 1. DMA基本概念 2. DMA传送过程 易错点 DMA控制器操作流程 3. DMA传送方式 这是单总线的结果 &#xff08;CPU说了算 所以不会产生于CPU的冲突&#xff09; 这…

idea创建dynamic web project

由于网课老师用的是eclipse,所以又得自己找教程了…… 解决方案&#xff1a; https://blog.csdn.net/Awt_FuDongLai/article/details/115523552

仕考网:公务员体检对视力有要求吗?

公务员招聘过程中的体检标准对视力有具体要求&#xff0c;根据不同的岗位职责有所差异。通常情况下&#xff0c;如果申请者双眼经过矫正后视力均低于4.8(小数视力0.6)&#xff0c;则会被视为不合格。 对于某些特殊岗位&#xff0c;如J察等&#xff0c;单侧裸眼视力若低于4.8也…

【教程】Github Page 添加自定义域名

目录 前言购买域名验证自定义域名映射自定义域名记录类型注意点参考 前言 Github Page 是 Github 提供的一个可以从 Github 仓库上托管静态网站的功能服务。默认当你建立起一个仓库后&#xff0c;在对应会有一个可供浏览的静态网站&#xff0c;例如 https://eternaldeath.gith…

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

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

MySQL架构优化及SQL优化

变更项目的整体架构是性能收益最大的方式。主要涉及两方面&#xff0c;一方面是从整个项目角度&#xff0c;引入一些中间件优化整体性能&#xff0c;另一方面是调整MySQL的部署架构&#xff0c;确保能承载更大的流量访问&#xff0c;提高数据层的整体吞吐。 1. 引入缓存中间件…

语义分割和实例分割区别?

语义分割&#xff1a;将图像中的每个像素分配给其对应的语义类别&#xff0c;其主要针对于像素&#xff0c;或者说它是像素级别的图像分割方法。&#xff1a;语义分割的目的是为了从像素级别理解图像的内容&#xff0c;并为图像中的每个像素分配一个对象类。 实例分割&#xf…

泛微E9开发 控制Radio框字段打印是否仅显示选中项文字

控制Radio框字段打印是否仅显示选中项文字 1、需求说明2、实现方法3、扩展知识点控制Radio框字段打印是否仅显示选中项文字格式参数说明样例 1、需求说明 当我们对单选框进行打印时&#xff0c;往往会把所有的选项一起打印出来&#xff08;如下图所示&#xff09;&#xff0c;现…

【1】A-Frame整体介绍

1.A-Frame是什么&#xff1f; A-Frame 是一个用于构建虚拟现实 (VR) 体验的 Web 框架。 A-Frame 基于 HTML 之上&#xff0c;因此上手简单。但 A-Frame 不仅仅是 3D 场景图或标记语言&#xff1b;它还是一种标记语言。其核心是一个强大的实体组件框架&#xff0c;为 Three.js …

昇思MindSpore学习入门-模型模块自定义

基础用法示例 神经网络模型由各种层(Layer)构成&#xff0c;MindSpore提供构造神经网络层的基础单元Cell&#xff0c;基于Cell进行神经网络封装。下面使用Cell构造经典模型AlexNet。 如图所示&#xff0c;AlexNet由5个卷积层与3个全连接层串联构成&#xff0c;我们使用mindspo…

秋招Java后端开发冲刺——并发篇2(ThreadLocal、Future接口)

本文对ThreadLocal类和Future接口进行了总结概括&#xff0c;包括ThreadLocal类的原理、内存泄露等问题&#xff0c;和Future接口的使用等问题。 一、ThreadLocal 1. 介绍 ThreadLocal&#xff08;线程局部变量&#xff09;是Java中的一个类&#xff0c;线程通过维护一个本地…

探索安卓四大组件:活动、服务、广播接收器和内容提供者

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Android ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 1. Activity&#xff08;活动&#xff09; 概述&#xff1a; 生命周期&#xff1a; 使用方法&#xff1a; 2. Service&…

简过网:2024年一级造价工程师正在报名中,看看你有报考资格吗?

计划报考2024一级造价工程师的小伙伴要注意了&#xff0c;现在一造报名正在启动中&#xff0c;想考试的小伙伴一定要看清楚这些报考条件和考试内容哦&#xff0c;今天&#xff0c;小编和大家一块来分享一下&#xff0c;希望对你有帮助。 几个简单的问题&#xff0c;让你彻底了解…