Spring源码学习(五):Spring AOP

免责声明

本人还处于学习阶段,如果内容有错误麻烦指出,敬请见谅!!!

Demo

在这里插入图片描述

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.8</version>
</dependency>

AOP标签解析源码

​   其实AOP的标签解析与component-scan的逻辑一样,都会走parseCustomElement方法:
在这里插入图片描述

​   进入parseCustomElement方法后,解析aop标签的步骤和解析component-scan标签的步骤就一模一样,先是得到命名空间处理器(handler),再是得到aop标签的解析器(parser),然后调用解析器的parse方法完成标签解析。

​   由于之前在component-scan详细说了handler和parser的获取过程,这里就简单提一下:
在这里插入图片描述

在这里插入图片描述

component-scan: ContextNamespaceHandler->ComponentScanBeanDefinitionParser
aop: AopNamespaceHandler->ConfigBeanDefinitionParser

​   让我们再进一步看看ConfigBeanDefinitionParserparse方法:

在这里插入图片描述

1.configureAutoProxyCreator干了什么

​   尝试向容器注入或者升级AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者bean定义,专门用于后续创建AOP代理对象,注意这里是bean定义嗷,并没有实例化。

​   configureAutoProxyCreator首先会调用AopNamespaceUtils工具类的registerAspectJAutoProxyCreatorIfNecessary方法:

在这里插入图片描述

1.1 registerAspectJAutoProxyCreatorIfNecessary

​   调用AopConfigUtilsregisterAspectJAutoProxyCreatorIfNecessary方法,通过方法名也很好理解,该方法的作用是如果需要的话注册一个代理对象创建者。该方法会进一步调用registerOrEscalateApcAsRequired方法,这里面才是真正创建代理创建者的逻辑。

​   registerOrEscalateApcAsRequired方法的底层逻辑是这样的:

1.在beanFactory中找有没有beanName为"org.springframework.aop.config.internalAutoProxyCreator"的beanDefinition,有的话就尝试升级。
2.没有的话创建AspectJAwareAdvisorAutoProxyCreator的beanDefiniton,并以"org.springframework.aop.config.internalAutoProxyCreator"为beanName存入beanFactory中

在这里插入图片描述

1.1.1 不同代理创建者的优先级是什么(findPriorityForClass)

​   从上述代码可以看到,如果beanFactory缓存命中,则会去比较当前类型的代理创建者和beanFactory中类型的代理创建者的优先级,findPriorityForClass方法的实现也蛮朴实无华的:

在这里插入图片描述

​   从registerAspectJAutoProxyCreatorIfNecessary方法中我们可以看到,我们指定的代理创建者类型是AspectJAwareAdvisorAutoProxyCreator,其实还有其他:

1.< tx:annotation-driven />标签或者@EnableTransactionManagement事物注解:InfrastructureAdvisorAutoProxyCreator.class;
2.< aop:config />标签:AspectJAwareAdvisorAutoProxyCreator.class;
3.< aop:aspectj-autoproxy />标签或者@EnableAspectJAutoProxy注解:AnnotationAwareAspectJAutoProxyCreator.class。

​   从findPriorityForClass实现逻辑来看,这三者的优先级是:

AnnotationAwareAspectJAutoProxyCreator>AspectJAwareAdvisorAutoProxyCreator>InfrastructureAdvisorAutoProxyCreator

​   这三个也有相同的父类:

在这里插入图片描述

1.2 useClassProxyingIfNecessary干了什么

​   在升级或者注册了自动代理创建者的bean定义之后,这一步用于解析、设置proxy-target-class和expose-proxy属性到这个bean定义的proxyTargetClassexposeProxy属性中。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.3 registerComponentIfNecessary干了什么

​   还记得我们在parse方法最开始的时候创建了一个CompositeComponentDefinition类型的对象吗,这里我们就用上了,将创建代理对象的beanDefiniton封装进BeanComponentDefinition中,然后再存入CompositeComponentDefinition里面。

在这里插入图片描述

2 parsePointcut方法解析< aop:pointcut/>切入点标签

​   parsePointcut方法干的事情其实挺好理解的,方法内部根据< aop:pointcut/>标签创建一个beanDefinition,然后将这个beanDefinifition存入beanFactory中,最后将beanDefinition封装进PointcutComponentDefinition,然后进行注册(其实就是放入我们再事先创建好的CompositeComponentDefinition)。

在这里插入图片描述

2.1 createPointcutDefinition

​   createPointcutDefinition方法主要是根据< aop:pointcut/>标签的expression属性创建一个RootBeanDefinition类型的beanDefinition,该beanDefinition对应的class为AspectJExpressionPointcut

在这里插入图片描述

3. parseAdvisor解析<aop:advisor/>通知器标签

​   < aop:advisor/>通知器标签,它和< aop:aspect >类似,相当于一个小切面,它的advice-ref属性可以指向一个通知类bean,该bean要求实现Advice相关接口,不需要单独配置通知,但接口只有前置通知、后置通知和异常通知的方法。

​   通常,advisor被用来管理事物,它的advice-ref属性配置对一个< tx:advice >的id引用,后面学习事物的时候就知道了。

​   类似的,parseAdvisor方法也会生成一个beanDefinition然后存入beanFactory中,也会注册进CompositeComponentDefinition中:

在这里插入图片描述

3.1 createAdvisorBeanDefinition

​   让我们看看advisor标签对应的beanDefinition:

在这里插入图片描述

3.2 parsePointcutProperty

​   parsePointcutProperty方法主要是获取切入点的信息:

在这里插入图片描述

4. parseAspect解析< aop:aspect/>切面标签

​   < aop:aspect/>标签用于配置切面,其内部可以定义具体的应用到哪些切入点的通知,这是一个非常重要的标签,相比其他两个标签,parseAspect方法稍微有些复杂:

在这里插入图片描述

在这里插入图片描述

4.1 parseAdvice:解析通知标签

​   让我们继续看看parseAdvice方法,该方法主要是用来解析通知标签,包括< aop:before/>、< aop:after/>、< aop:after-returning/>、< aop:after-throwing/>、< aop:around/>,解析的最终bean定义同样会注入到容器中:

在这里插入图片描述

​   可以看到parseAdvice方法创建了一系列的beanDefinition,我们结合Xml配置文件看看,parseAdvice方法干了什么事情:

<aop:aspect id="aspect" ref="aspectMethod"><aop:before method="aspect" pointcut-ref="pointcut"/><aop:after method="aspect" pointcut-ref="pointcut"/><aop:after method="aspect" pointcut-ref="pointcut"/>
</aop:aspect>
  1. 首先会创建MethodLocatingFactoryBean类型的beanDefinition,并向该beanDefinition中添加属性methodName,其值为method属性的值。添加targetBeanName属性,其值为父标签aspect的ref属性的值。(内部bean,不会注册到beanFactory中)
  2. 创建SimpleBeanFactoryAwareAspectInstanceFactory类型的beanDefinition,并添加aspectBeanName属性,其值为父标签aspect的ref属性的值。SimpleBeanFactoryAwareAspectInstanceFactory是一个实例工厂,专门用于获取切面实例对象,也就是通知类对象。(内部bean,不会注册到beanFactory中)
  3. 将上面两个beanDefiniton作为参数传入createAdviceDefinition中,该方法返回的也是一个beanDefinition,表示advice通知标签的beanDefinition,也就是< aop:before/>、< aop:after/>对应的beanDefinition。(内部bean,不会注册到beanFactory中)
  4. 再创建一个AspectJPointcutAdvisor类型的beanDefinition,这是切入点通知器bean定义,把第三步中的beanDefinition作为构造器参数添加至bean定义中。(注册到beanFactory中)
4.1.1 createAdviceDefinition创建通知bean定义

​   createAdviceDefinition方法内部会根据不同通知的类型创建不同类型的beanDefinition,然后设置beanDefinition中的属性值。特殊的是,方法内部还会设置构造器参数:

在这里插入图片描述

​   我们进入到getAdviceClass方法内部可以看到,不同通知类型对应着不同类型的class:

在这里插入图片描述

​   从类的继承关系来看,这五个通知类型都继承至AbstractAspectJAdvice

在这里插入图片描述

​   其实这五个子类的构造方法都是调用父类的构造方法,因此这五个通知类型对应的class不一样,但是它们的构造器参数却是一样的:

在这里插入图片描述


AspectJAwareAdvisorAutoProxyCreator创建代理对象

​   之前我们已经学习了aop标签解析的相关源码,标签解析主要干了两件事:首先创建了一个创建代理对象的对象(AspectJAwareAdvisorAutoProxyCreator)主要是用来后面创建代理对象的,然后就是解析aop的子标签,创建并注册相关的beanDefinition。

​   接下来我们具体来看看,Spring是如何用AspectJAwareAdvisorAutoProxyCreator创建代理对象的。

写在前面

​   我们先来看看AspectJAwareAdvisorAutoProxyCreator实现了哪些接口,可以看到这些接口在我们Spring初始化启动源码分析时都有讲到过,特别是BeanPostProcessor这个超级接口,我们会回调里面的方法干很多事情。

在这里插入图片描述

​   让我们来稍微回顾一下Spring初始化的流程,主要是看Spring对后处理器的处理流程:

在这里插入图片描述

1. postProcessBeforeInstantiation方法

​   实际上是执行AspectJAwareAdvisorAutoProxyCreator的父类AbstractAutoProxyCreator的方法:

在这里插入图片描述

1.1 getCacheKey:根据beanName和beanClass获取cacheKey

​   getCacheKey方法实现比较简单,先判断beanName是否为空,不为空的话再判断是不是FactoryBean,是FactoryBean则返回&+beanName,不是FactoryBean则返回beanName。

​   如果beanName为空,则返回beanClass。

在这里插入图片描述

1.2 isInfrastructureClass:是否是Spring AOP基础结构类

​   如果当前beanClass是Spring AOP的基础结构类,则isInfrastructureClass返回true,不进行代理。默认实现是将Advice、Pointcut、Advisor、AopInfrastructureBean接口及其实现均作为AOP基础结构类。

在这里插入图片描述

1.3 shouldSkip:是否跳过代理

​   该方法在AspectJAwareAdvisorAutoProxyCreator中的逻辑如下:

在这里插入图片描述

​   该方法在父类AbstractAutoProxyCreator中的逻辑如下:如果beanName以beanCassName开头,并且以“.ORIGINAL”结束,那么返回true,否则返回false。

在这里插入图片描述

1.4 getCustomTargetSource:获取自定义目标源

​   获取当前bean的自定义目标源TargetSource。如果获取到了TargetSource,那么将会直接在postProcessBeforeInstantiation方法中创建代理,避免没必要的Spring初始化和实例化流程,同时后面的postProcessAfterInitialization方法中不会再创建代理对象。

​   但是通常情况下我们并未设置TargetSource,因此不会走这个逻辑创建代理对象,创建代理对象的逻辑还是在postProcessAfterInitialization中。

1.5 postProcessBeforeInstantiation方法的调用时机

​   当我们把AspectJAwareAdvisorAutoProxyCreator的beanDefinition加载到beanFactory中,并实例化后,我们再来看看createBean方法的部分逻辑:

在这里插入图片描述

​   让我们继续进到resolveBeforeInstantiation方法里面看看,很明显重点在于applyBeanPostProcessorsBeforeInstantiationapplyBeanPostProcessorsAfterInitialization方法,这方法内部分别会回调postProcessBeforeInstantiationpostProcessAfterInstantiation方法:

在这里插入图片描述

​   值得注意的是AspectJAwareAdvisorAutoProxyCreator后处理器属于InstantiationAwareBeanPostProcessorapplyBeanPostProcessorsBeforeInstantiation里面会回调postProcessBeforeInstantiation方法,在这个方法内部会缓存advisedBean。因此advisedBean实例化时,会在这里被缓存到AspectJAwareAdvisorAutoProxyCreator后处理器里面。

​   但是由于不会设置TargetSource,因此这里返回的bean通常为null,不会进一步执行applyBeanPostProcessorsAfterInstantiation方法。

2. postProcessAfterInitialization方法

​   postProcessAfterInitialization包含了创建代理对象的核心方法,核心逻辑在wrapIfNecessary里面:

在这里插入图片描述

2.1 wrapIfNecessary方法

​   在文章开头给出的案例中,对于AspectTarget会执行到第四步,进行代理对象的创建:

在这里插入图片描述

2.1.1 getAdvicesAndAdvisorsForBean:获取可用Advisors

​   获取当前bean(本案例中指的是AspectTarget)可用的Advisor通知器,该方法由子类实现。在解析**< aop:config/>标签时,< aop:advisor/>标签的DefaultBeanFactoryPointcutAdvisor< aop:declare-parents/>**标签的DeclareParentsAdvisor,通知标签的AspectJPointcutAdvisor,他们都属于Advisor类型,也就是通知器,通常一个切入点和一个通知方法就组成通知器。

​   在本案例中其实主要的Advisor就是通知标签的AspectJPointcutAdvisor

在这里插入图片描述

​   可以看到getAdvicesAndAdvisorsForBean方法内部的核心方法是findEligibleAdvisors来获得当前bean的可用通知器Advisors,我们继续来看看findEligibleAdvisors方法的具体实现:

在这里插入图片描述

2.1.1.1 findAdvisorsThatCanApply:找到可应用当前bean的Advisor

​   findAdvisorsThatCanApply主要是来找到可以应用到当前bean的Advisor,会分别对**< aop:declare-parents/>**和通知标签做处理,判断是否可用于当前bean也很简单,根据切入点表达式来对类和方法进行匹配即可:在这里插入图片描述

2.1.1.2 extendAdvisors:扩展通知器链

​   添加一个特殊的Advisor到Advisors链头部,使用 AspectJ 切入点表达式和使用 AspectJ 样式的Advice时都需要添加,添加的是ExposeInvocationInterceptor.ADVISOR,实际类型是一个DefaultPointcutAdvisor类型的单例实例,它内部保存了ExposeInvocationInterceptor拦截器的单例实例,当进行拦截时Advisors链的第一个方法就是调用该拦截器的invoke方法。

​   在本案例中,执行完这一步后,对于AspectTarget的Advisor链,应该有四个Advisor(3个通知Advisor+1个特殊Advisor)。

在这里插入图片描述

2.1.2 createProxy:创建代理对象

​   为给定的 bean 创建 AOP 代理。这一步会选择到底是通过JDK代理创建还是通过CGLIB代理创建。

在这里插入图片描述

2.1.2.1 evaluateProxyInterfaces:是否有合理接口

​   如果当前接口不是一个容器回调接口,并且当前接口不是内部语言接口,并且接口方法个数至少为1个(不是标志性接口)。同时满足上面三个条件,当前接口就是一个合理的代理接口。

​   在本案例中,因为AspectTarget没有合理接口,因此会在这一步将proxyTargetClass属性设置为true,最后会走CGLIB代理模式。

在这里插入图片描述

2.1.2.2 getProxy:获取代理对象

​   获取代理对象分为两步:首先我们需要创建对应类型的AopProxy(JDK or CGLIB),然后再根据AopProxy获取代理对象。

在这里插入图片描述

​   我们先看看createAopProxy方法,Spring最终会调用DefaultAopProxyFactorycreateAopProxy方法:

在这里插入图片描述

​   当我们得到了AopProxy,我们可以进一步获取代理对象,根据AopProxy的不同,获取代理对象的方式也不同。

​   我们先看看JDK代理模式获取代理对象:

在这里插入图片描述

​   再看看CGLIB代理模式获取代理对象,稍微复杂一点:

在这里插入图片描述

​   再看看createProxyClassAndInstance方法:

在这里插入图片描述

代理对象的增强方法的执行原理

写在前面

​   还是以文章开头的Demo为例,我们获取目标类(AspectTarget)的Bean时,Spring容器会返回给我们AspectTarget的代理对象:

在这里插入图片描述

​   可以看到,我们的Demo最终还是走的是CGLIB代理模式,由于JDK和CGLIB在方法增强的逻辑其实差不多,我们就简单分析一下CGLIB执行的原理,其基本思想是:责任链模式和方法递归调用来实现方法的代理和增强的逻辑。

增强入口:DynamicAdvisedInterceptor的intercept方法

​   进一步地,获取到代理对象后,我们调用目标类的target方法,最终会被第一个拦截器DynamicAdvisedInterceptor拦截,调用里面的intercept方法:

在这里插入图片描述

​   以文章开头的Demo为例,现在代理对象有四个Advisor(一个特殊+三个通知),特殊Advisor会匹配所有方法,三个通知Advisor匹配目标类的target方法,我们看看chain中有哪些东西:

在这里插入图片描述

增强逻辑:CglibMethodInvocation

​   在找到匹配当前方法的Advisor后,我们继续看看增强方法的调用逻辑,首先来看看CglibMethodInvocationproceed方法,其实是调用了父类ReflectiveMethodInvocationproceed方法:

在这里插入图片描述

​   我们看看Demo中对应的三个类型的拦截器的invoke方法:

在这里插入图片描述

​   我们要明白两件事,首先是tryreturnfinally的执行顺序:

try中的代码块(包括return语句中的代码,但是不执行return返回操作)> finally代码块 > return操作

​   其次是

mi.proceed()最后执行的还是ReflectiveMethodInvocation的proceed方法

​   让我们来看看Demo中的递归栈变化和增强方法的执行:

在这里插入图片描述

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

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

相关文章

外包干了6年,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入杭州某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了6年的功能测试…

24/11/5 算法笔记adagrad 自适应学习率

AdaGrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种用于随机优化的算法&#xff0c;它通过适应每个参数的学习率来优化目标函数。 自适应学习率&#xff1a; AdaGrad算法的核心特点是为每个参数自适应地调整学习率。这意味着每个参数都有自己的学习率&#xff…

逆向之断点和找解密方法

企名片科创平台 先找到解密内容 ctrlshiftF搜索关键字,一般用一个函数包裹的就是解密方法 有2个方法调用,给其中一个打上断点刷新页面,为什么要打断点?为什么不打断点我就没有办法在控制台直接输出变量的值或者调用函数呢&#xff1f;个人理解这时候i只是一个局部变量&#x…

【云备份】httplib库

目录 1.httplib库简介 2.httplib请求类 3.httplib响应类 4.Server类 5.Client类 6.httplib库搭建简单服务器 6.1.ubuntu20.04使用防火墙开放端口 6.2.效果 7.httplib库搭建简单服务器 注意&#xff1a;如果对HTTP不熟悉就去&#xff1a;【网络】HTTP_yum install telne…

【CENet】多模态情感分析的跨模态增强网络

在MSA领域&#xff0c;文本的准确度远远高于音频和视觉&#xff0c;如果文本能达到90%&#xff0c;那么音频和视觉的准确度只有60%~80%&#xff0c;但是过往研究很少针对情感分析的背景下去提高音频和视频的准确度。 abstract&#xff1a; 多模态情感分析&#xff08;MSA&…

多线程--模拟实现定时器--Java

一、定时器的概念 定时器的本质就是一个闹钟&#xff0c;时间到了开始执行某些逻辑。Java标准库中的定时器是Timer。 我们查阅Java文档可以详细看到定时器的使用方法&#xff1a; Timer最核心的方法就是schedule方法。值得注意的是我们通常描述任务是使用Runnable来描述&…

‌MySQL中‌between and的基本用法‌

文章目录 一、between and语法二、使用示例2.1、between and数值查询2.2、between and时间范围查询2.3、not between and示例 BETWEEN AND操作符可以用于数值、日期等类型的字段&#xff0c;包括边界值。 一、between and语法 MySQL中的BETWEEN AND操作符用于在两个值之间选择…

视频一键转换3D:Autodesk 发布 Video to 3D Scene

Video 3D Scene 最近 Autodesk 旗下公司 Wonder Dynamics 推出了 Wonder Animation 的测试版&#xff0c;它使用突破性的视频到 3D 场景技术&#xff0c;通过将任何视频序列转换为 3D 动画场景来加速动画电影的制作。 Video 3D Scene Video 3D Scene 生成效果 作为 Wonder Stud…

数据结构 C/C++(实验一:线性表)

&#xff08;大家好&#xff0c;今天分享的是数据结构的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 提要&#xff1a;实验题目 一、实验目的 二、实验内容及要求 三、算法思想 实验1 实验2 四、源程序及注释 …

关于SQLServer在局域网内无法连接的问题的解决思路

针对SQL Server 2008在局域网内无法连接的问题&#xff0c;以下是一些详细的解决办法。我们在过程中需要用到Microsoft SQL Server 2008和Microsoft SQL Server tools 2008数据库软件中的配置管理器以及SQL Server Management Studio工具&#xff0c;入下截图所示。 一、检查网…

【C++】RBTree——红黑树

文章目录 一、红黑树的概念1.1 红⿊树的规则&#xff1a;1.2 理解最长路径长度不超过最短路径长度的 2 倍1.3 红⿊树的效率 二、 红⿊树的实现2.1 红⿊树的结构2.2 红⿊树的插⼊2.2.1 红⿊树树插⼊⼀个值的⼤概过程 2.3 红⿊树的插⼊代码实现 一、红黑树的概念 红⿊树是⼀棵⼆…

Docker-- cgroups资源控制实战

上一篇&#xff1a;容器化和虚拟化 什么是cgroups&#xff1f; cgroups是Linux内核中的一项功能&#xff0c;最初由Google的工程师提出&#xff0c;后来被整合进Linux内核; 它允许用户将一系列系统任务及其子任务整合或分隔到按资源划分等级的不同组内&#xff0c;从而为系统…

vscode ssh连接autodl失败

autodl服务器已开启&#xff0c;vscode弹窗显示连接失败 0. 检查状态 这里的端口和主机根据自己的连接更改 ssh -p 52165 rootregion-45.autodl.pro1. 修改config权限 按返回的路径找到config文件 右键--属性--安全--高级--禁用继承--从此对象中删除所有已继承的权限--添加…

你适合哪种tiktok广告账户类型?

TikTok在广告营销方面的分类体系极为详尽。在开设广告账户时&#xff0c;根据不同的海外市场和商品类型&#xff0c;TikTok会有各自的开户标准。此外&#xff0c;广告主所开设的TikTok广告账户类型会直接影响其可投放的广告类型。在广告出价方面&#xff0c;广告主的营销目标不…

大规模语言模型:从理论到实践(1)

1、绪论 大规模语言模型&#xff08;Large Language Models&#xff0c;LLM&#xff09;是由包含数百亿以上参数的深度神经网络构建的语言模型&#xff0c;采用自监督学习方法通过大量无标注文本进行训练。自2018年以来&#xff0c;多个公司和研究机构相继发布了多种模型&#…

SpringBoot中@Validated或@Valid注解校验的使用

文章目录 SpringBoot中Validated或Valid注解校验的使用1. 添加依赖2. 使用示例准备2-1 测试示例用到的类2-2 实体Dto&#xff0c;加入校验注解2-2 Controller 3. 示例测试4. Valid 和 Validated注解详解4-1 常用规则注解4-2 分组验证4-2-1 示例准备4-2-2 Controller接口4-2-3 P…

HarmonyOS使用arkTS拉起指定第三方应用程序

HarmonyOS使用arkTS拉起指定第三方应用程序 前言代码及说明bundleName获取abilityName获取 前言 本篇只说采用startAbility方式拉起第三方应用&#xff0c;需要用到两个必备的参数bundleName&#xff0c;abilityName&#xff0c;本篇就介绍如何获取参数… 代码及说明 bundle…

04_CC2530+Uart串口通信

04_CC2530UART串口通信 串口通信基本概念 串行通信: 数据字节一位位地依次传送的通信方式, 串行通信的速度慢, 但用的传输线条数少, 成本低&#xff0c;适用于远距离的数据传送并行通信: 数据字节的各位同事传送的通信方式, 优点是数据传送速度快, 缺点是占用的传输线条数多,…

Speaker Recognition说话人识别(声纹识别)

说话人识别&#xff0c;又称声纹识别。从上世纪60年代开始到现在&#xff0c;声纹识别一直是生物识别技术研究的主题。从传统的基于模板匹配的方法&#xff0c;到早期基于统计学方法&#xff0c;直到基于深度学习的声纹识别技术成为主流。本项目给出一个从传统&#xff08;基于…

SpringBoot篇(简化操作的原理)

目录 一、代码位置 二、统一版本管理&#xff08;parent&#xff09; 三、提供 starter简化 Maven 配置 四、自动配置 Spring&#xff08;引导类&#xff09; 五、嵌入式 servlet 容器 一、代码位置 二、统一版本管理&#xff08;parent&#xff09; SpringBoot项目都会继…