【JavaEE进阶】Spring AOP使用篇

目录

1.AOP概述

2.SpringAOP快速入门

2.1 引入AOP依赖

2.2 编写AOP程序

3. Spring AOP详解

3.1 Spring AOP 核心概念

3.1.1切点(Pointcut)

3.1.2 连接点 (Join Point)

3.1.3 通知(Advice)

3.1.4 切面(Aspect)

3.2 通知类型

3.3@PointCut

3.4 切面优先级

3.5 切点表达式

3.5.1 execution 表达式

3.5.2 @annotation

3.5.2.1 自定义注解 @MyAspect

3.5.2.2 切面类

3.5.2.3 添加自定义注解

4. Spring AOP的实现方式


1.AOP概述

AOP是Spring框架的一大核心(第一大核心是loC)

什么是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 的作用: 在程序执行期间不修改源代码的基础上对已有方法的增强(无侵入性: 解耦)

接下来我们来看看 SpringAOP 如何来实现.

2.SpringAOP快速入门

学习完什么是 AOP 后, 我们先通过下面的程序体验下 AOP 的开发, 并掌握 Spring 中 AOP 的开发步骤.

需求: 统计图书系统中各个接口方法的执行时间.

2.1 引入AOP依赖

在 pom.xml 文件中添加配置

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

2.2 编写AOP程序

记录 Controller 中每个方法的执行时间

先看看传统的方法:

/**
     * 根据ID查询图书信息
     *
     * @param bookId
     * @return
     */
    @RequestMapping("/queryBookById")
    public BookInfo queryBookById(Integer bookId) {
        log.info("根据ID查询图书信息, id:{}", bookId);
        long start = System.currentTimeMillis();
        BookInfo bookInfo = bookService.queryBookById(bookId);
        long end = System.currentTimeMillis();
        log.info("[BookController] queryBookById 耗时: " + (end-start) + " ms");
        return bookInfo;
    }

使用这种方式, 当要测试多个接口的时候, 就需要对每个接口的代码进行修改 , 工作量比较复杂, 很影响我们的时间和效率.

下面来看看使用AOP的代码吧:

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;

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

运行程序, 观察日志

对程序进行简单的讲解:

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

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

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

整个代码划分为三部分

我们通过AOP入门程序完成了业务接口执行耗时的统计.

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

  • 代码无侵入: 不修改原始的业务方法, 就可以对原始的业务方法进行了功能的增强或者是功能的改变
  • 减少了重复代码
  • 提高开发效率
  • 维护方便
     

3. Spring AOP详解

下面我门再来详细学习AOP, 主要是一下几个部分

Spring AOP 中涉及的核心概念

Spring AOP 的通知类型

多个 AOP 的执行顺序

3.1 Spring AOP 核心概念

3.1.1切点(Pointcut)

切点(Pointcut), 也称之为 "切入点"

Pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述), 告诉程序对哪些方法来进行功能增强.

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

3.1.2 连接点 (Join Point)

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

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

package com.example.demo.controller;

@RequestMapping("/book")
@RestController
public class BookController {
    @RequestMapping("/addBook")
    public Result addBook(BookInfo bookInfo) {
        //...代码省略 
    }

    @RequestMapping("/queryBookById")
    public BookInfo queryBookById(Integer bookId) {
        //...代码省略 
    }

    @RequestMapping("/updateBook")
    public Result updateBook(BookInfo bookInfo) {
        //...代码省略 
    }
}

上述 BookController 中的方法都是连接点

切点和连接点的关系

连接点是满足切点表达式的元素, 切点可以看作是保存了众多连接点的一个集合.

比如:

切点表达式: 全体教师

连接点就是: 张三, 李四等各个教室

3.1.3 通知(Advice)

通知就是具体要做的事情, 指哪些重复的逻辑, 也就是共性功能(最终体现为一个方法)

比如上述程序中记录业务方法的耗时时间, 就是通知

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

3.1.4 切面(Aspect)

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

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

切面既包含了通知逻辑的定义, 也包括了连接点的定义.

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

3.2 通知类型

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

Spring 中 AOP 的通知类型有一下几种:

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

@Before: 前置通知, 次注解表述的通知方法在目标方法前被执行

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

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

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

接下来我门通过代码来加深对这几个通知的理解:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component
@Slf4j
@Aspect
public class AspectDemo {
    //前置通知
    @Before("execution(* com.example.aop.controller.*.*(..))")
    public void doBefore() {
        log.info("AspectDemo do before...");
    }
    //后置通知
    @After("execution(* com.example.aop.controller.*.*(..))")
    public void doAfter() {
        log.info("AspectDemo do after...");
    }
    //环绕通知
    @Around("execution(* com.example.aop.controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("AspectDemo do around before...");
        Object result = null;
        try {
            result = joinPoint.proceed();
        }catch (Exception e) {
            log.error("do around 执行目标函数, 内部发生异常");
        }
        log.info("AspectDemo do around after...");
        return result;
    }
    //返回后通知
    @AfterReturning("execution(* com.example.aop.controller.*.*(..))")
    public void doAfterReturning() {
        log.info("AspectDemo do AfterReturning...");
    }
    //抛出异常后通知
    @AfterThrowing("execution(* com.example.aop.controller.*.*(..))")
    public void doAfterThrowing() {
        log.info("AspectDemo do AfterThrowing...");
    }
}

写一些测试程序:

import com.example.aop.config.MyAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

    @RequestMapping("/t2")
    public String t2() {
        log.info("执行t2方法....");
        int a = 10/0;
        return "t2";
    }
}

运行程序, 观察日志:

1.正常运行的情况

观察日志

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

从上图也可以看出来, @Around 标识的通知方法包含两个部分, 一个 "前置逻辑" , 一个 "后置逻辑" .其中 "前置逻辑" 会先于 @Before 标识的通知方法执行. "后置逻辑" 会晚于 @After 标识的通知方法执行

2. 异常时的情况

观察日志

程序发生异常的情况下:

@AfterReturning 标识的通知方法不会执行, @AfterThrowing 标识的通知方法执行了

@Around 环绕通知中原始方法调用时有异常, 通知中的环绕后的代码也不会在执行了(因为原始方法调用出异常了)

注意事项:

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

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

一个切面类可以有多个切点

3.3@PointCut

上面代码存在一个问题, 就是存在大量重复的切点表达式 execution(* com.example.demo.controller.*.*(..)) , Spring 提供了 @Pointcut 注解, 把公共的切点表达式提取出来, 需要用到时引入该切点表达式即可.

上述代码就可以修改为:

@Component
@Slf4j
@Aspect
public class AspectDemo {
    @Pointcut("execution(* com.example.aop.controller.*.*(..))")
    public void pt(){};

    //前置通知
    @Before("pt()")
    public void doBefore() {
        //...代码省略
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        //...代码省略
    }
    //添加环绕通知
    @Around("pt()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //...代码省略
    }
    //返回后通知
    @AfterReturning("pt()")
    public void doAfterReturning() {
        //...代码省略
    }
    //抛出异常后通知
    @AfterThrowing("pt()")
    public void doAfterThrowing() {
        //...代码省略
    }
}

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

@Component
@Slf4j
@Aspect
public class AspectDemo2 {

    @Before("com.example.aop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("AspectDemo2 do before...");
    }

    @After("com.example.aop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("AspectDemo2 do after...");
    }
}

3.4 切面优先级

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

我们还是通过程序来验证:

定义多个切⾯类:

为了防止干扰, 我们把AspectDemo这个切面先去掉(把@Component注解去掉就可以)

为了简单化, 只写了@Before 和 @After 两个通知

@Component
@Slf4j
@Aspect
public class AspectDemo2 {

    @Before("com.example.aop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("AspectDemo2 do before...");
    }

    @After("com.example.aop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("AspectDemo2 do after...");
    }
}
@Component
@Slf4j
@Aspect
public class AspectDemo3 {
    @Before("com.example.aop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("AspectDemo3 do before...");
    }

    @After("com.example.aop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("AspectDemo3 do after...");
    }
}
@Component
@Slf4j
@Aspect
public class AspectDemo4 {

    @Before("com.example.aop.aspect.AspectDemo.pt()")
    public void doBefore() {
        log.info("AspectDemo4 do before...");
    }

    @After("com.example.aop.aspect.AspectDemo.pt()")
    public void doAfter() {
        log.info("AspectDemo4 do after...");
    }
}

运行程序, 访问接口:

 观察日志:

通过上述程序的运行结果, 可以看出:

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

  • @Before 通知: 字母名字靠前的先执行
  • @After 通知: 字母排名靠前的后执行

但这种方式不方便管理,我们的类名更多还是具备一定含义的.

Spring给我们提供了一个新的注解,来控制这些切面通知的执行顺序: @Order

使用方式如下:

重新运行程序, 观察日志:

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

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

@Before 通知: 数字越小先执行

@After 通知: 数字越大先执行

@Order 控制切面的优先级, 先执行优先级高的切面, 在执行优先级较低的切面, 最终执行目标方法.

3.5 切点表达式

上面的代码中,我们一直在使用切点表达式来描述切点. 下面我们来介绍一下切点表达式的语法.

切点表达式常见有两种表达方式

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

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

3.5.1 execution 表达式

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

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

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

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

匹配特定方法名且有特定参数的方法:

execution(* myMethod(String, int))

 匹配特定方法名且有特定参数, 并且抛出特定异常的方法

execution(* myMethod(String, int) throws IOExeception)

3.5.2 @annotation

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

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

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

实现步骤:

1. 编写自定义注解

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

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

准备测试代码

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

    @RequestMapping("/t2")
    public String t2() {
        log.info("执行t2方法....");
        int a = 10/0;
        return "t2";
    }
}
@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";
    }
}
3.5.2.1 自定义注解 @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 被保留的时间长短, 标明注解的声明周期

@Retention的取值有三种

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

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

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

3.5.2.2 切面类

使用 @annotation 切点表达式定义切点, 只对 @MyAspect 生效

切面类代码如下:

@Component
@Slf4j
@Aspect
public class MyAspectDemo {
    //前置通知
    @Before("@annotation(com.example.aop.config.MyAspect)")
    public void before() {
        log.info("MyAspect -> before...");
    }
    //后置通知
    @After("@annotation(com.example.aop.config.MyAspect)")
    public void after() {
        log.info("MyAspect -> after...");
    }
}
3.5.2.3 添加自定义注解

在 TestController 中 t1() 和 UserController 中的 u1() 这两个方法上添加自定义注解 @MyAspect, 其他方法不加

运行程序, 测试接口:

观察日志:

继续测试 t2, 观察日志:

切面通知未执行

4. Spring AOP的实现方式

1.基于注解 @Aspect

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

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

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

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

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

相关文章

经典神经网络(13)GPT-1、GPT-2原理及nanoGPT源码分析(GPT-2)

经典神经网络(13)GPT-1、GPT-2原理及nanoGPT源码分析(GPT-2) 2022 年 11 月&#xff0c;ChatGPT 成功面世&#xff0c;成为历史上用户增长最快的消费者应用。与 Google、FaceBook等公司不同&#xff0c;OpenAI 从初代模型 GPT-1 开始&#xff0c;始终贯彻只有解码器&#xff0…

安卓webview内h5页面调用录音设置

h5页面调用录音接口getUserMeia在webview中有可能不成功&#xff0c;进入错误回调&#xff0c;这个时候webview尽可能设置下面这些权限就会好。

Linux系统编程(七)进程间通信IPC

进程间通讯的7种方式_进程间通信的几种方法-CSDN博客 管道 pipe&#xff08;命名管道和匿名管道&#xff09;&#xff1b;信号 signal&#xff1b;共享内存&#xff1b;消息队列&#xff1b;信号量 semaphore&#xff1b;套接字 socket&#xff1b; 1. 管道 内核提供&#x…

华为云安全防护,九河云综合分解优劣势分析

随着全球化的发展&#xff0c;越来越多的企业开始寻求在国际市场上扩展业务&#xff0c;这一趋势被称为企业出海。然而&#xff0c;企业在海外扩张面临诸多隐患与安全挑战&#xff0c;其中因为地域的不同&#xff0c;在安全性方面与国内相比会变得薄弱&#xff0c;从而导致被黑…

展开说说:Android列表之RecyclerView

RecyclerView 它是从Android5.0出现的全新列表组件&#xff0c;更加强大和灵活。用于显示列表形式 (list) 或者网格形式 (grid) 的数据&#xff0c;替代ListView和GridView成为Android主流的列表组件。可以说Android客户端只要有表格的地方就有RecyclerView。 RecyclerView 内…

无痛接入FastText算法进行文本分类(附代码)

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…

未来已来,如何打造智慧养殖场?

近年来&#xff0c;国家出台了一系列扶持政策&#xff0c;以促进养殖行业高质量发展&#xff0c;推动行业转型升级。在国家政策和市场需求的双重驱动下&#xff0c;养殖行业正迎来前所未有的发展机遇。智慧养殖以其高效、智能和可持续的特点&#xff0c;正逐步取代传统养殖方式…

【消息队列】RabbitMQ集群原理与搭建

目录 前言1、集群搭建1.1、安装RabbitMQ1.1.1、前置要求1.1.2、安装Erlang环境①创建yum库配置文件②加入配置内容③更新yum库④正式安装Erlang 1.1.3、安装RabbitMQ1.1.4、RabbitMQ基础配置1.1.5、收尾工作 1.2、克隆VMWare虚拟机1.2.1、目标1.2.2、克隆虚拟机1.2.3、给新机设…

涉案财物管理系统|DW-S405系统实现涉案财物科学化管理

随着社会的不断发展&#xff0c;犯罪形式日益复杂&#xff0c;涉案财物的种类和数量也不断增加。传统的涉案财物管理方式已经无法满足现代执法办案的需求。因此&#xff0c;建立一套科学、高效、规范的警用涉案财物管理系统成为公安机关亟待解决的问题。 涉案财物管理系统DW-S…

哈希表 | 哈希查找 | 哈希函数 | 数据结构 | 大话数据结构 | Java

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;毛毛张今天分享的内容&#x1f586;是数据结构中的哈希表&#xff0c;毛毛张主要是依据《大话数据结构&#x1f4d6;》的内容来进行整理&#xff0c;不…

vue的学习--day2

如有错误&#xff0c;烦请指正~ 目录 一、什么是单页面应用程序 二、使用工具&#xff1a;node.js 三、工具链 易错点 一、什么是单页面应用程序 多个组件&#xff08;例如登录、注册等以vue结尾的都叫做组件&#xff09;在一个页面显示&#xff0c;叫单页面应用…

Linux C 程序 【02】创建线程

1.开发背景 上一个篇章&#xff0c;基于 RK3568 平台的基础上&#xff0c;运行了最简单的程序&#xff0c;然而我们使用了 Linux 系统&#xff0c;系统自带的多线程特性还是比较重要的&#xff0c;这个篇章主要描述线程的创建。 2.开发需求 设计实验&#xff1a; 创建一个线程…

极验行为式验证码适配HarmonyOS 鸿蒙SDK下载

现阶段&#xff0c;越来越多的开发者正在积极加入鸿蒙生态系统。随着更多开发者的参与&#xff0c;早在去年9月&#xff0c;极验就成为首批拥有鸿蒙NEXT内测版本和手机系统测试机会的验证码供应商。 为了提高各开发者及企业客户集成鸿蒙版本行为验4.0的效率&#xff0c;方便大家…

Android 通知组

一. 通知组简介 从 Android 7.0&#xff08;API 级别 24&#xff09;开始&#xff0c;您可以在一个组中显示相关通知。如下所示: 图 1. 收起&#xff08;顶部&#xff09;和展开&#xff08;底部&#xff09;的通知组。 注意 &#xff1a;如果应用发出 4 条或更多条通知且未…

大数据平台需要存算分离吗?某保险集团:以 ZBS 优化资源利用率,缩短业务用时超一半

金融机构普遍采用“存算一体”架构支撑基于 Hadoop 框架的大数据平台。而随着金融业务的多元化发展&#xff0c;不同业务对计算和存储的需求差异较大&#xff0c;由于“存算一体”架构共享存储与计算资源&#xff0c;经常会出现资源需求不均衡、资源利用率低下、难以灵活调度等…

工具篇:鸿蒙DevEco Studio5.0版本下载及安装

1、下载中心地址 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生态 2、安装 DevEco Studio支持Windows和macOS系统&#xff0c;下面将针对两种操作系统的软件安装方式分别进行介绍。 Windows环境 运行环境要求 为保证DevEco Studio正常运行&#…

Mysql需要知道的点

目录 一、数据库的三范式是什么 二、Mysql数据库引擎有哪些 三、说说Innodb与MYISAM的区别 四、数据库的事务 五、索引是什么 六、优化手段有哪些 七、简单说一说 drop&#xff0c;delete与truncate的区别 八、什么是视图 九、什么是内连接、左外连接、右外连接&#x…

Ubuntu20.04使用Samba

目录 一、Samba介绍 Samba 的主要功能 二、启动samba 三、主机操作 四、Ubuntu与windows系统中文件互联 五、修改samba路径 一、Samba介绍 Samba 是一个开源软件套件&#xff0c;用于在 Linux 和 Unix 系统上实现 SMB&#xff08;Server Message Block&#xff09;协议…

[行业原型] Web端原型案例:康欣医疗后台管理系统

​医疗管理系统是一个业务复杂&#xff0c;功能庞大的系统&#xff0c;以下为HIS医院管理系统的常见模块&#xff0c;供大家参考。 本周为大家带来Web端原型案例&#xff1a;康欣医疗后台管理系统&#xff0c;先上原型&#xff1a; 完整文档加班主任微信号 添加班主任回复 “1…

ansible常用模块详解

一、Ansible 1.1 简介 Ansible是自动化运维工具&#xff0c;能实现跨主机对应用编排管理部署。 Ansible能批量配置、部署、管理上千台主机&#xff0c;是应用级别的跨主机编排工具。 比如以前需要切换到每个主机上执行的一或多个操作&#xff0c;使用Ansible只需在固定的一…