SSM框架,spring-aop的学习

news2025/6/28 7:16:47

代理模式

二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

可以将重复的非核心的代码写在代理类中,将核心的代码写在目标方法中,每次通过调用代理类的方法来间接调用目标方法,以方便统一管理重复的非核心的代码。

静态代理

每个目标方法都有其对应的代理类中的代理方法,重复的非核心的代码都写在代理类中,例:

public class StaticProxyCalculator implements Calculator {
    private Calculator calculator;

    public StaticProxyCalculator(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int i, int j) {
        System.out.println("i = " + i + ", j = " + j);
        int result = calculator.add(i,j);
        System.out.println("result = " + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        System.out.println("i = " + i + ", j = " + j);
        int result = calculator.sub(i,j);
        System.out.println("result = " + result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        System.out.println("i = " + i + ", j = " + j);
        int result = calculator.mul(i,j);
        return result;
    }

    @Override
    public int div(int i, int j) {
        System.out.println("i = " + i + ", j = " + j);
        int result = calculator.div(i,j);
        System.out.println("result = " + result);
        return 0;
    }
}

但是这种方法还是有大量重复,也没有很好地解决统一管理的问题,也不具备灵活性,所以要使用动态代理的方法

动态代理

动态代理技术分类

  • JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口,他会根据目标类的接口动态生成一个代理对象,代理对象和目标对象有相同的接口。
  • cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。

以jdk代理为例(了解即可)

代理类:

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy(){

        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:加载动态生成的代理类的类加载器
         * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * proxy:代理对象
                 * method:代理对象需要实现的方法,即其中需要重写的方法
                 * args:method所对应方法的参数
                 */
                Object result = null;
                try {
                    System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                } finally {
                    System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                }
                return result;
            }
        };

        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}
public class Test {
    /**
     * 静态代理
     */
    @org.junit.jupiter.api.Test
    public void test1(){
        Calculator target = new CalculatorPureImpl();
        Calculator calculator = new StaticProxyCalculator(target);
        calculator.add(3,4);
    }

    /**
     * 动态代理
     */
    @org.junit.jupiter.api.Test
    public void test2(){
        //创建目标对象
        Calculator target = new CalculatorPureImpl();
        //获取代理对象
        ProxyFactory factory = new ProxyFactory(target);
        Calculator proxy = (Calculator) factory.getProxy();
        proxy.add(3,4);
    }
}

不需要编写代理代码,我们可以使用Spring AOP实现代理

面向切面编程(AOP)

面向切面编程(Aspect Oriented Programming)是对面向对象编程的补充和完善。面向对象编程是通过纵向的继承和实现来实现功能的管理。而面向切面编程是将代码中的重复的非核心的业务提取到一个公共的地方,通过动态代理技术横向插入到各个方法中去。解决非核心代码的冗余问题。

关于AOP的几个名词

横切关注点

从每一个方法中抽取出来的同一类非核心业务。业务处理的主要流程是核心关注点,其他非主要流程就是横切关注点。

通知(增强)

每一个横切关注点上要做的事情都需要一个方法来实现,这样的方法称之为通知方法

  • 前置通知:在被代理的目标方法前执行
  • 返回通知:在被代理的目标方法成功结束后执行
  • 异常通知:在被代理的目标方法异常结束后执行
  • 后置通知:在被代理的目标方法最终结束后执行
  • 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

连接点

指那些被拦截的点,在Spring中,指可以被动态代理拦截目标类的方法

切入点

定位连接点的方式,被选中的连接点

切面(aspect)

切入点和通知的结合,指的是通知类

目标(target)

被代理的目标对象

代理(proxy)

向目标对象通知之后创建的代理对象

织入(weave)

指把通知应用到目标上,生成代理对象的过程。可以再编译期间织入,也可以再运行期间织入,spring使用运行期间织入

Spring AOP框架

Spring AOP框架,是基于AOP编程思维,封装动态代理技术,简化动态代理技术实现的框架。SpringAOP内部帮助实现动态代理,只需写少量的配置,指定生效范围即可,即可完成面向切面思维编程的实现

aop只能对ioc容器内的对象创建代理对象,代理对象会存储到ioc容器

基于注解方式实现

底层技术组成

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口。
  • cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。
  • AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

aop的实现步骤:

  1. 正常写核心业务和配置ioc
  2. 定义增强类,定义增强方法
  3. 配置增强类
  4. 开启aop的配置

aop大致实现例子:

增强类:

/**
 *内部存储增强代码
*/
@Component
@Aspect
public class LogAdvice {
    @Before("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")
    public void start(){
        System.out.println("方法开始了");
    }
    @After("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")
    public void after(){
        System.out.println("方法结束了");
    }
    @AfterThrowing("execution(public int com.ergou.service.impl.CalculatorPureImpl.add(int,int))")
    public void error(){
        System.out.println("方法报错了");
    }
}

配置类配置aop

@Configuration
@ComponentScan("com.ergou")
//让程序允许使用AspectJ注解
@EnableAspectJAutoProxy
public class JavaConfig {
}

xml文件配置aop(和配置类二选一)

<context:component-scan base-package="com.ergou"/>
<aop:aspectj-autoproxy/>

原来的业务代码不需要做改动

测试:

@SpringJUnitConfig(value = JavaConfig.class)
public class Test {
//
@Autowired
    private Calculator calculator;
    @org.junit.jupiter.api.Test
    public void test1(){
        int add = calculator.add(3,4);
        System.out.println(add);
    }
}

注:使用jdk的动态代理,代理类和目标对象会有同样的接口,要使用代理功能,要用目标对象的接口获取ioc容器中的对象(获取到的将会是代理对象而不是目标对象,也证明ioc中真正存储的对象是代理对象而不是目标对象)

cglib的生效场景是目标方法的类没有实现接口

五种常见的通知注解

  • @Before: 前置通知
  • @AfterReturning:后置通知
  • @Around: 环绕通知
  • @AfterThrowing:异常通知
  • @After :最终通知

获取通知细节信息

需要获取方法签名、传入的实参等信息时,可以在通知方法声明JoinPoint类型的形参。

  • JoinPoint 接口通过 getSignature() 方法获取目标方法的签名(方法声明时的完整信息)
  • 通过目标方法签名对象获取方法名
  • 通过 JoinPoint 对象获取外界调用目标方法时传入的实参列表组成的数组
@Before("execution(* com.ergou.service.impl.*.*(..))")
public void start(JoinPoint joinPoint){
//获取方法所属的目标类的信息
String simpleName = joinPoint.getTarget().getClass().getSimpleName();
    System.out.println(simpleName);
//获取方法名称
String name = joinPoint.getSignature().getName();
    System.out.println(name);
//获取访问修饰符
int modifiers = joinPoint.getSignature().getModifiers();
    String s = Modifier.toString(modifiers);
//获取参数列表
Object[] args = joinPoint.getArgs();
    for (Object o :
            args) {
        System.out.println(o);
    }
}

获取返回结果,要在@AfterReturning注解标注的方法内进行,先在方法的形参列表中加一个Object类型的形参变量(假设变量名取为result)用来接收返回值信息,在@AfterReturning注解中,加上属性returning,其值为接收返回值信息的形参变量名result,例:

@AfterReturning(value = "execution(* com.ergou.service.impl.*.*(..))",
                returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
    System.out.println(result);
}

获取异常信息,与获取返回结果类似。要在@AfterThrowing注解标注的方法内进行,先在方法的形参列表中加一个Throwable类型的形参变量(假设变量名取为throwable)用来接收异常信息,在@AfterThrowing注解中加上属性throwing,其值为接收异常信息的形参变量名throwable,例:

@AfterThrowing(value = "execution(* com.ergou.service.impl.*.*(..))",
              throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint,Throwable throwable){
    throwable.getStackTrace();
}

切点表达式

切点表达式作用

AOP切点表达式(Pointcut Expression)是一种用于指定切点的语言,它可以通过定义匹配规则,来选择需要被切入的目标对象

切点表达式写在通知注解的value属性中,如果没有其他属性,可以省略value=

切点表达式语法

 

语法细节

  • ..不能作为开头,可以写做*..
  • 参数列表部分,如果写做(String..),意思是参数列表只要开头的第一个参数是String类型的即可,后面随意。(..String)即为结尾为String类型的参数,前面随意。(String..int)同理。

切点表达式的提取和复用

当前类中提取:

步骤:

  1. 定义一个空方法,加上@Pointcut,在注解的括号中写入指定的切点表达式
  2. 在通知注解中的value属性中直接写@Pointcut注解标记的方法的方法名加括号即可

例:

@Aspect
@Component
public class MyAdvice {
    @Pointcut("execution(* com.ergou.service.impl.*.*(..))")
    public void pc(){

    }
    @Before("pc()")
    public void start(JoinPoint joinPoint){
//获取方法所属的目标类的信息
String simpleName = joinPoint.getTarget().getClass().getSimpleName();
        System.out.println(simpleName);
//获取方法名称
String name = joinPoint.getSignature().getName();
        System.out.println(name);
//获取访问修饰符
int modifiers = joinPoint.getSignature().getModifiers();
        String s = Modifier.toString(modifiers);
//获取参数列表
Object[] args = joinPoint.getArgs();
        for (Object o :
                args) {
            System.out.println(o);
        }
    }
    @AfterReturning(value = "pc()",
                    returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println(result);
    }
    @After("pc()")
    public void after(){

    }
    @AfterThrowing(value = "pc()",
                  throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint,Throwable throwable){
        throwable.getStackTrace();
    }
}

创建一个存储切点的类单独维护切点表达式:

  1. 创建一个类,用来存储切点表达式,里面同样用@Pointcut注解来存储切点表达式
  2. 通知注解中的value属性中写上要引用的切点表达式所在类的全限定符加上.方法名加括号

例:

@Component
public class MyPointcut {
    @Pointcut("execution(* com.ergou.service.impl.*.*(..))")
    public void pc(){

    }
}

这个存储切点表达式的类同样也要放进ioc容器

@AfterReturning(value = "com.ergou.pointcut.MyPointcut.pc()",
                returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
    System.out.println(result);
}

环绕通知

环绕通知对应整个 try...catch...finally 结构,包括前面四种通知的所有功能。

环绕通知使用步骤:

  1. 在通知类中写一个环绕通知方法,参数列表中写一个ProceedingJoinPoint类型的参数,此参数会自动接收目标方法的信息。并在此环绕通知方法上方写上@Around注解,其value属性指定切点表达式
  2. 环绕通知需要在通知中,定义目标方法的执行。目标方法的执行代码为Object result = joinPoint.proceed(args)。其中,result是用来接收目标方法的返回值的对象,因为result最后还要被环绕通知方法返回;args是传入目标方法的参数,通过调用joinPoint对象的相关方法来获取。
  3. 用 try...catch...finally 结构将执行代码围绕起来,就可以在相应的位置放通知功能的代码
  4. 最后return返回接收了目标方法的返回值的result对象

例:

/**
 *环绕通知需要在通知中,定义目标方法的执行
* @paramjoinPoint接收了目标方法的信息 比起之前的JoinPoint类型的对象,多了一个执行的功能
* @return
 */
@Around("com.ergou.pointcut.MyPointcut.pc()")
public Object transaction(ProceedingJoinPoint joinPoint){
    Object[] args = joinPoint.getArgs();
    Object result = null;
    try {
//前置通知代码
System.out.println("开启事务");
        result = joinPoint.proceed(args);
//后置通知代码
System.out.println("提交事务");
    } catch (Throwable e) {
//异常通知代码
System.out.println("事务回滚");
        throw new RuntimeException(e);
    } finally {
//最终通知代码
System.out.println("必须执行的代码");
    }
    return result;
}

切面优先级设置

切面优先级是指要调用目标方法时,如果有多个切面,优先级高的切面的通知方法是在外层。

使用@Order注解标记在通知类上,在其中指定一个数字,值越小,优先级越高。

@Component
@Aspect
//值越小,优先级越高
@Order(2)
public class TxAdvice {
    @Before("com.ergou.pointcut.MyPointcut.pc()")
    public void begin(){

    }
    @AfterReturning("com.ergou.pointcut.MyPointcut.pc()")
    public void commit(){

    }
    @AfterThrowing("com.ergou.pointcut.MyPointcut.pc()")
    public void rollback(){

    }
}

优先级高的因为在外层,所以前置先执行,后置后执行

xml方式配置aop

了解即可,主要使用注解方式

<!-- 配置目标类的bean -->
<bean id="calculatorPure" class="com.atguigu.aop.imp.CalculatorPureImpl"/>

<!-- 配置切面类的bean -->
<bean id="logAspect" class="com.atguigu.aop.aspect.LogAspect"/>

<!-- 配置AOP -->
<aop:config>
    <!-- 配置切入点表达式 -->
    <aop:pointcut id="logPointCut" expression="execution(* *..*.*(..))"/>

    <!-- aop:aspect标签:配置切面 -->
    <!-- ref属性:关联切面类的bean -->
    <aop:aspect ref="logAspect">
        <!-- aop:before标签:配置前置通知 -->
        <!-- method属性:指定前置通知的方法名 -->
        <!-- pointcut-ref属性:引用切入点表达式 -->
        <aop:before method="printLogBeforeCore" pointcut-ref="logPointCut"/>

        <!-- aop:after-returning标签:配置返回通知 -->
        <!-- returning属性:指定通知方法中用来接收目标方法返回值的参数名 -->
        <aop:after-returning
                method="printLogAfterCoreSuccess"
                pointcut-ref="logPointCut"
                returning="targetMethodReturnValue"/>

        <!-- aop:after-throwing标签:配置异常通知 -->
        <!-- throwing属性:指定通知方法中用来接收目标方法抛出异常的异常对象的参数名 -->
        <aop:after-throwing
                method="printLogAfterCoreException"
                pointcut-ref="logPointCut"
                throwing="targetMethodException"/>

        <!-- aop:after标签:配置后置通知 -->
        <aop:after method="printLogCoreFinallyEnd" pointcut-ref="logPointCut"/>

        <!-- aop:around标签:配置环绕通知 -->
        <!--<aop:around method="……" pointcut-ref="logPointCut"/>-->
    </aop:aspect>
</aop:config>

根据类型装配 bean

  1. 情景一

    • bean 对应的类没有实现任何接口
    • 根据 bean 本身的类型获取 bean
      • 测试:IOC容器中同类型的 bean 只有一个

        正常获取到 IOC 容器中的那个 bean 对象

      • 测试:IOC 容器中同类型的 bean 有多个

        会抛出 NoUniqueBeanDefinitionException 异常,表示 IOC 容器中这个类型的 bean 有多个

  2. 情景二

    • bean 对应的类实现了接口,这个接口也只有这一个实现类
      • 测试:根据接口类型获取 bean
      • 测试:根据类获取 bean
      • 结论:上面两种情况其实都能够正常获取到 bean,而且是同一个对象
  3. 情景三

    • 声明一个接口
    • 接口有多个实现类
    • 接口所有实现类都放入 IOC 容器
      • 测试:根据接口类型获取 bean

        会抛出 NoUniqueBeanDefinitionException 异常,表示 IOC 容器中这个类型的 bean 有多个

      • 测试:根据类获取bean

        正常

  4. 情景四(jdk代理情景)

    • 声明一个接口

    • 接口有一个实现类

    • 创建一个切面类,对上面接口的实现类应用通知

      • 测试:根据接口类型获取bean

        正常

      • 测试:根据类获取bean

        无法获取 原因分析:

    • 应用了切面后,真正放在IOC容器中的是代理类的对象

    • 目标类并没有被放到IOC容器中,所以根据目标类的类型从IOC容器中是找不到的

  5. 情景五(cglib代理场景)

    1. 声明一个类
    2. 创建一个切面类,对上面的类应用通知
      1. 测试:根据类获取 bean,能获取到

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

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

相关文章

【51单片机实验笔记】开关篇(二) 矩阵按键

目录 前言原理图分析矩阵按键扫描算法 软件实现1. 矩阵键盘检测2. 简易计算器实现 总结 前言 本节内容&#xff0c;我们学习一下矩阵按键&#xff0c;它是独立按键的阵列形式&#xff0c;常见的应用即键盘。 本节涉及到的封装源文件可在《模块功能封装汇总》中找到。 本节完…

MySQL性能分析1

1、查看执行频次 查看当前数据库的INSERT,UPDATE,DELETE,SELECT的访问频次&#xff0c;得到当前数据库是以插入&#xff0c;更新和删除为主还是以查询为主&#xff0c;如果是以插入&#xff0c;更新和删除为主的话&#xff0c;那么优化比重可以轻一点儿。 语法&#xff1a; …

Muse专业版教程:制作简谱,制作吉他谱

UP教你制作吉他谱,muse专业版吉他谱制作过程分享_哔哩哔哩_bilibili教学讲解-小宁视频制作-狂奔的琴弦软件-Muse专业版后面会分集录从零开始制作吉他谱,感兴趣的小伙伴点一波关注, 视频播放量 15457、弹幕量 1、点赞数 208、投硬币枚数 127、收藏人数 424、转发人数 59, 视频…

Day-02-02

Httpclient测试 安装HTTP Client插件 使用IDEA自带的http接口测试工具——HTTP Client Open in HTTP Client 生成测试用例 点击绿色箭头可以运行测试用例&#xff0c;控制台会输出结果。 保存和修改测试用例 在模块下新建一个api-test包用来存放测试用例&#xff0c;将生…

【测试】测试概念篇和基础篇

目 录 一.了解软件测试的基础概念1.需求2.测试用例3.BUG 二.开发模型和测试模型1.瀑布模型2.螺旋模型3.增量模型和迭代模型4.敏捷模型 三.软件测试模型V模型W模型 四.BUG篇1. 如何合理的创建 bug2. bug 级别3. bug 的生命周期4. 跟开发产生争执怎么办 一.了解软件测试的基础概念…

关于项目中websocket的socket.io客户端js库的应用

1.如何使用客户端js库? pnpm add socket.io-client2.如何建立连接&#xff1f; import io from socket.io-client // 参数1&#xff1a;不传默认是当前服务域名&#xff0c;开发中传入服务器地址 // 参数2&#xff1a;配置参数&#xff0c;根据需要再来介绍 const socket i…

Java学习第十六节之创建对象内存分析和小结类与对象

创建对象内存分析 小结类与对象 package oop;//一个项目应该只存在一个main方法 public class Application {public static void main(String[] args) {/*1.类与对象类是一个模版&#xff1a;抽象对象是一个具体的实例2.方法定义&#xff0c;调用&#xff01;3.对应的引用引用…

【vscode】在vscode中如何导入自定义包

只需要额外添加这两条语句即可&#xff1a; import os,sys sys.path.append("../..") 需要注意的是&#xff0c;ipynb 文件打开的工作目录是文件本身的路径&#xff0c;而 py 文件打开的工作路径是 vscode 打开的路径。 相比较而言 pycharm 中创建好项目之后并不…

51单片机编程应用(C语言):DS1302实时时钟

单片机计时的缺陷&#xff1a; 1.他的精度不高&#xff0c;没有时钟芯片精度高&#xff0c; 2.会占用单片机CPU的时间&#xff0c; 3.单片机的时钟无法掉电继续运行&#xff0c;&#xff08;最大的缺点&#xff09; DS1302芯片内部有备用电池&#xff0c;可以掉电继续计时…

前端JS按钮点击事件、跳出弹窗、遮罩的实战示例

前端JS 按钮事件、弹窗、遮罩实战示例 文章目录 前端JS 按钮事件、弹窗、遮罩实战示例一、开始二、功能实现三、具体代码如下1、运行结果2、具体代码如下 四、功能解析1、index.html2、button.css3、server.js 一、开始 各位未来的开发者请上座&#xff0c;闲暇的时候发现&…

蓝桥杯:C++二分算法

在基本算法中&#xff0c;二分法的应用非常广泛&#xff0c;它是一种思路简单、编程容易、效率极高的算法。蓝桥杯软件类大赛中需要应用二分法的题目很常见。 二分法有整数二分和实数二分两种应用场景 二分法的概念 二分法的概念很简单&#xff0c;每次把搜索范围缩小为上一…

普中51单片机学习(六)

点亮第一个LED LED相关知识 LED,即发光二极管&#xff0c;是一种半导体固体发光器件。工作原理为&#xff1a;LED的工作是有方向性的&#xff0c;只有当正级接到LED阳极&#xff0c;负极接到LED的阴极的时候才能工作&#xff0c;如果反接LED是不能正常工作的。其原理图如下 …

linux系统监控工具prometheus的安装以及监控mysql

prometheus 安装服务端客户端监控mysql prometheus浏览器查看 安装 https://prometheus.io/download/下载客户端和服务端以及需要监控的所有的包服务端 官网下载下载prometheustar -xf prometheus-2.47.2.linux-amd64.tar.gz -C /usr/local/ cd /usr/local/ mv prometheus-2.…

如何理解CSS的边框宽度?

CSS 边框宽度学习手记 CSS 边框宽度小概念 在CSS的世界里&#xff0c;border-width这个属性真的很实用&#xff0c;它能帮我指定HTML元素四周边框的宽度。这个宽度嘛&#xff0c;可以用像素px、点pt、厘米cm、相对单位em这些来表示&#xff0c;很方便吧&#xff01;还有呢&am…

代码随想录 Leetcode435. 无重叠区间

题目&#xff1a; 代码(首刷看解析 2024年2月17日&#xff09;&#xff1a; class Solution { private:const static bool cmp(vector<int>& a,vector<int>& b) {return a[0] < b[0];} public:int eraseOverlapIntervals(vector<vector<int>&…

XUbuntu22.04之apt与snap如何重装软件(二百一十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

[AIGC_coze] Kafka 的主题分区之间的关系

Kafka 的主题分区之间的关系 在 Kafka 中&#xff0c;主题&#xff08;Topics&#xff09;和分区&#xff08;Partitions&#xff09;是两个重要的概念&#xff0c;它们之间存在着密切的关系。 主题是 Kafka 中用于数据发布和订阅的逻辑单元。每个主题可以包含多个分区&#x…

《学成在线》微服务实战项目实操笔记系列(P92~P120)【下】

史上最详细《学成在线》项目实操笔记系列【下】&#xff0c;跟视频的每一P对应&#xff0c;全系列18万字&#xff0c;涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳&#xff0c;参考这篇&#xff0c;相信会带给你极大启发。 四、课程发布模块 4.1 (课程发布)模块需求…

Ubuntu学习笔记-Ubuntu搭建禅道开源版及基本使用

文章目录 概述一、Ubuntu中安装1.1 复制下载安装包路径1.2 将安装包解压到ubuntu中1.3 启动服务1.4 设置开机自启动 二、禅道服务基本操作2.1 启动&#xff0c;停止&#xff0c;重启&#xff0c;查看服务状态2.2 开放端口2.3 访问和登录禅道 卜相机关 卜三命、相万生&#xff0…

天锐绿盾|防泄密系统|计算机文件数据\资料安全管理软件

“天锐绿盾”似乎是一款专注于防泄密和计算机文件数据/资料安全管理的软件。在信息安全日益受到重视的今天&#xff0c;这样的软件对于保护企业的核心数据资产和防止敏感信息泄露至关重要。 通用地址&#xff1a;www.drhchina.com 防泄密系统的主要功能通常包括&#xff1a; 文…