1.【Spring实战——面向切面的切面切面Spring】1.5使用注解创建切面
2.ç»èAOPç论
3.76 张图,剖析 Spring AOP 源码,核心核心小白居然也能看懂,源码源码大神,切面切面请收下我的核心核心膝盖!
4.aspect切面是源码源码html源码get什么
5.如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志
6.面试官:你讲讲AOP与OOP有什么区别?
【Spring实战——面向切面的切面切面Spring】1.5使用注解创建切面
使用注解创建切面是Spring实战中的重要技巧,它简化了AspectJ 5以前繁琐的核心核心Java语言扩展,使得任何类可以轻易转换为切面。源码源码通过定义切面,切面切面我们可以将非核心关注点(如观众的核心核心行为)与核心功能分离,提高代码的源码源码可维护性。
在定义切面时,切面切面首先使用@AspectJ注解标记Audience类,核心核心其中的源码源码三个通知方法(takeSeats, silence CellPhones, applause, demandRefund)利用@Before, @AfterReturning, @AfterThrowing注解表明执行时机。这些注解都引用了一个切点表达式,但重复使用可能会显得冗余。为了解决这个问题,@PointCut注解允许我们在切面内部定义可重用的切点,如performance()方法。
Audience作为注解切面,通过Spring自动代理功能,当装配为bean时,其注解通知将自动应用到匹配的bean上。启用自动代理的方式可以是JavaConfig或XML配置。Spring的AspectJ自动代理实际上是一个基于代理的实现,尽管使用了注解,但它仍然局限于代理方法的调用。
要充分利用AspectJ的能力,需要在运行时启用AspectJ,而不是依赖Spring。环绕通知是强大的,它可以包裹目标方法,实现前置和后置通知的java强制转换源码组合。通过创建一个环绕通知,如watchPerformance(),我们可以在一个方法中统一处理前后行为。
切面处理参数的方法可以是通过参数化的通知,如记录磁道播放次数的TrackCounter切面。引入新功能可以通过AOP的引入概念,为Spring bean添加新的方法,即使这些方法在原始实现中不存在。
总的来说,注解创建切面提供了简洁的编程方式,但源码的可访问性是其使用的一个前提。在Spring中,注解和自动代理简化了切面声明,但也需要一定的条件。
ç»èAOPç论
1ãä»ä¹æ¯AOP
AOP(Aspect Orient Programming) : é¢ååé¢ç¼ç¨
Aspect ï¼è¡¨ç¤ºåé¢ï¼ ç»ä¸å¡æ¹æ³å¢å çåè½ï¼å«ååé¢ã åé¢ä¸è¬é½æ¯éä¸å¡åè½ï¼ èä¸åé¢åè½ä¸è¬é½æ¯å¯ä»¥å¤ç¨çã ä¾å¦ æ¥å¿åè½ï¼ äºå¡åè½ï¼ æéæ£æ¥ï¼åæ°æ£æ¥ï¼ ç»è®¡ä¿¡æ¯ççã
Orientï¼é¢åï¼å¯¹ç
Programmingï¼ç¼ç¨ã
2ã æä¹ç解é¢ååé¢ç¼ç¨ ï¼ ä»¥åé¢ä¸ºæ ¸å¿è®¾è®¡å¼åä½ çåºç¨ã
1ï¼è®¾è®¡é¡¹ç®æ¶ï¼ æ¾åºåé¢çåè½ã
2ï¼å®æåé¢çæ§è¡æ¶é´ï¼ æ§è¡çä½ç½®ã
3ãAOPçä½ç¨ï¼
1ï¼è®©åé¢åè½å¤ç¨
2ï¼è®©å¼å人åä¸æ³¨ä¸å¡é»è¾ã æé«å¼åæç
3ï¼å®ç°ä¸å¡åè½åå ¶ä»éä¸å¡åè½è§£è¦åã
4ï¼ç»åå¨çä¸å¡æ¹æ³ï¼ å¢å åè½ï¼ä¸ç¨ä¿®æ¹åæ¥ç代ç
4ãAOPä¸æ¯è¯
1ï¼Aspectï¼åé¢ï¼ ç»ä¸å¡æ¹æ³å¢å çåè½ã
2ï¼JoinPoint:è¿æ¥ç¹ï¼ è¿æ¥åé¢çä¸å¡æ¹æ³ã å¨è¿ä¸ªä¸å¡æ¹æ³æ§è¡æ¶ï¼ä¼åæ¶æ§è¡åé¢çåè½ã
3ï¼Pointcut: åå ¥ç¹ï¼ æ¯ä¸ä¸ªæå¤ä¸ªè¿æ¥ç¹éåã 表示è¿äºæ¹æ³æ§è¡æ¶ï¼é½è½å¢å åé¢çåè½ã
表示åé¢æ§è¡çä½ç½®ã
4ï¼target: ç®æ å¯¹è±¡ï¼ ç»é£ä¸ªå¯¹è±¡å¢å åé¢çåè½ï¼ è¿ä¸ªå¯¹è±¡å°±æ¯ç®æ 对象ã
5ï¼Adviceï¼éç¥ï¼å¢å¼ºï¼ï¼è¡¨ç¤ºåé¢çæ§è¡æ¶é´ã å¨ç®æ æ¹æ³ä¹åæ§è¡åé¢ï¼è¿æ¯ç®æ æ¹æ³ä¹åæ§è¡åé¢ã
AOPä¸éè¦çä¸ä¸ªè¦ç´ ï¼ Aspectï¼ Pointcut , Advice. è¿ä¸ªæ¦å¿µçç解æ¯ï¼ å¨Adviceçæ¶é´ï¼å¨Pointcutçä½ç½®ï¼ æ§è¡Aspect
AOPæ¯ä¸ä¸ªå¨æçææ³ã å¨ç¨åºè¿è¡æé´ï¼å建代çï¼ServcieProxyï¼ï¼ä½¿ç¨ä»£çæ§è¡æ¹æ³æ¶ï¼å¢å åé¢çåè½ãè¿ä¸ªä»£ç对象æ¯åå¨å åä¸çã
5ãä»ä¹æ¶åä½ æ³ç¨AOP
ä½ è¦ç»æäºæ¹æ³ å¢å ç¸åçä¸äºåè½ã æºä»£ç ä¸è½æ¹ã ç»ä¸å¡æ¹æ³å¢å éä¸å¡åè½ï¼ä¹å¯ä»¥ä½¿ç¨AOP
6ãAOPææ¯ææ³çå®ç°
使ç¨æ¡æ¶å®ç°AOPã å®ç°AOPçæ¡æ¶æå¾å¤ã æåç两个
1ï¼ Spring ï¼ Springæ¡æ¶å®ç°AOPææ³ä¸çé¨ååè½ã Springæ¡æ¶å®ç°AOPçæä½æ¯è¾ç¹çï¼æ¯éã
2ï¼ Aspectj ï¼ ç¬ç«çæ¡æ¶ï¼ä¸é¨æ¯AOPã å±äºEclipse
7ãéç¥
Aspectj表示åé¢æ§è¡æ¶é´ï¼ç¨çéç¥ï¼Adviceï¼ã è¿ä¸ªéç¥å¯ä»¥ä½¿ç¨æ³¨è§£è¡¨ç¤ºã
讲5ä¸ªæ³¨è§£ï¼ è¡¨ç¤ºåé¢ç5个æ§è¡æ¶é´ï¼ è¿äºæ³¨è§£å«åéç¥æ³¨è§£ã
@Before ï¼ åç½®éç¥
@AfterRetunringï¼ åç½®éç¥
@Around: ç¯ç»éç¥
@AfterThrowing:å¼å¸¸éç¥
@After:æç»éç¥
8ãPointcut ä½ç½®
Pointcut ç¨æ¥è¡¨ç¤ºåé¢æ§è¡çä½ç½®ï¼ 使ç¨Aspectjä¸åå ¥ç¹è¡¨è¾¾å¼ã
张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!
本文将简要介绍AOP(面向切面编程)的基础知识与使用方法,并深入剖析Spring AOP源码。首先,我们需要理解AOP的基本概念。
1. **基础知识
**1.1 **什么是AOP?
**AOP全称为Aspect Oriented Programming,即面向切面编程。AOP的思想中,周边功能(如性能统计、日志记录、事务管理等)被定义为切面,核心功能与切面功能独立开发,然后将两者“编织”在一起,这就是AOP的核心。
AOP能够将与业务无关、b站专栏源码却为业务模块共同调用的逻辑封装,减少系统重复代码,降低模块间的耦合度,有利于系统的可扩展性和可维护性。
1.2 **AOP基础概念
**解释较为官方,以下用“方言”解释:AOP包括五种通知分类。
1.3 **AOP简单示例
**创建`Louzai`类,添加`LouzaiAspect`切面,并在`applicationContext.xml`中配置。程序入口处添加`"睡觉"`方法并添加前置和后置通知。接下来,我们将探讨Spring内部如何实现这一过程。
1.4 **Spring AOP工作流程
**为了便于理解后面的源码,我们将整体介绍源码执行流程。整个Spring AOP源码分为三块,结合示例进行讲解。
第一块是前置处理,创建`Louzai`Bean前,遍历所有切面信息并存储在缓存中。第二块是后置处理,创建`Louzai`Bean时,主要处理两件事。第三块是执行切面,通过“责任链+递归”执行切面。
2. **源码解读
**注意:Spring版本为5.2..RELEASE,否则代码可能不同!这里,我们将从原理部分开始,逐步深入源码。
2.1 **代码入口
**从`getBean()`函数开始,进入创建Bean的逻辑。
2.2 **前置处理
**主要任务是遍历切面信息并存储。
这是显卡计算模式源码重点!请务必注意!获取切面信息流程结束,后续操作都从缓存`advisorsCache`获取。
2.2.1 **判断是否为切面
**执行逻辑为:判断是否包含切面信息。
2.2.2 **获取切面列表
**进入`getAdvice()`,生成切面信息。
2.3 **后置处理
**主要从缓存拿切面,与`Louzai`方法匹配,创建AOP代理对象。
进入`doCreateBean()`,执行后续逻辑。
2.3.1 **获取切面
**首先,查看如何获取`Louzai`的切面列表。
进入`buildAspectJAdvisors()`,方法用于存储切面信息至缓存`advisorsCache`。随后回到`findEligibleAdvisors()`,从缓存获取所有切面信息。
2.3.2 **创建代理对象
**有了`Louzai`的切面列表,开始创建AOP代理对象。
这是重点!请仔细阅读!这里有两种创建AOP代理对象方式,我们选择使用Cglib。
2.4 **切面执行
**通过“责任链+递归”执行切面与方法。
这部分逻辑非常复杂!接下来是“执行切面”最核心的逻辑,简述设计思路。
2.4.1 **第一次递归
**数组第一个对象执行`invoke()`,参数为`CglibMethodInvocation`。
执行完毕后,继续执行`CglibMethodInvocation`的`process()`。
2.4.2 **第二次递归
**数组第二个对象执行`invoke()`。
2.4.3 **第三次递归
**数组第三个对象执行`invoke()`。烈焰之痕源码
执行完毕,退出递归,查看`invokeJoinpoint()`执行逻辑,即执行主方法。回到第三次递归入口,继续执行后续切面。
切面执行逻辑已演示,直接查看执行方法。
流程结束时,依次退出递归。
2.4.4 **设计思路
**这部分代码研究了大半天,因为这里不是纯粹的责任链模式。
纯粹的责任链模式中,对象内部有一个自身的`next`对象,执行当前对象方法后,启动`next`对象执行,直至最后一个`next`对象执行完毕,或中途因条件中断执行,责任链退出。
这里`CglibMethodInvocation`对象内部无`next`对象,通过`interceptorsAndDynamicMethodMatchers`数组控制执行顺序,依次执行数组中的对象,直至最后一个对象执行完毕,责任链退出。
这属于责任链,实现方式不同,后续会详细剖析。下面讨论类之间的关系。
主对象为`CglibMethodInvocation`,继承于`ReflectiveMethodInvocation`,`process()`的核心逻辑在`ReflectiveMethodInvocation`中。
`ReflectiveMethodInvocation`的`process()`控制整个责任链的执行。
`ReflectiveMethodInvocation`的`process()`方法中,包含一个长度为3的数组`interceptorsAndDynamicMethodMatchers`,存储了3个对象,分别为`ExposeInvocationInterceptor`、`MethodBeforeAdviceInterceptor`、`AfterReturningAdviceInterceptor`。
注意!这3个对象都继承了`MethodInterceptor`接口。
每次`invoke()`调用时,都会执行`CglibMethodInvocation`的`process()`。
是否有些困惑?别着急,我将再次帮你梳理。
对象与方法的关系:
可能有同学疑惑,`invoke()`的参数为`MethodInvocation`,没错!但`CglibMethodInvocation`也继承了`MethodInvocation`,可自行查看。
执行逻辑:
设计巧妙之处在于,纯粹的责任链模式中,`next`对象需要保证类型一致。但这里3个对象内部没有`next`成员,不能直接使用责任链模式。怎么办呢?就单独设计了`CglibMethodInvocation.process()`,通过无限递归`process()`实现责任链逻辑。
这就是我们为什么要研究源码,学习优秀的设计思路!
3. **总结
**本文首先介绍了AOP的基本概念与原理,通过示例展示了AOP的应用。之后深入剖析了Spring AOP源码,分为三部分。
本文是Spring源码解析的第三篇,感觉是难度较大的一篇。图解代码花费了6个小时,整个过程都沉浸在代码的解析中。
难度不在于抠图,而是“切面执行”的设计思路,即使流程能走通,将设计思想总结并清晰表达给读者,需要极大的耐心与理解能力。
今天的源码解析到此结束,有关Spring源码的学习,大家还想了解哪些内容,欢迎留言给楼仔。
aspect切面是什么
aspect切面是面向切面编程,是一种思想,也是一套规则,像用户验证之类的都是可以这么理解,如果项目中很多地方需要验证用户是否登录,那么进行统一设置就可以不需要单独写代码。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志
首先,我们来观察一下切面日志的输出效果。在了解实现方法之前,我们可以看到每个请求的开始与结束都很清晰,同时打印了以下参数:
效果看起来还不错,接下来我们将一步步实现它。
二、添加 AOP Maven 依赖
在项目的 pom.xml 文件中,添加以下依赖:
三、自定义日志注解
接下来,我们来定义一个日志注解,如下所示:
源代码如下:
到这里,一个完整的自定义注解就定义完成了。
四、配置 AOP 切面
在配置 AOP 切面之前,我们需要了解一些 aspectj 相关注解的作用。
定义好切点后,我们可以围绕这个切点进行操作。接下来,定义一个 WebLogAspect.java 切面类,并声明一个切点。
然后,定义 @Around 环绕,用于何时执行切点。
接下来,看看 @Before 方法。
最后,用 @After 来做收尾。在每个接口的最后,打印日志结束标志。到这里,切面相关的代码就完成了。
五、如何使用?
因为我们的切点是自定义注解 @WebLog,所以我们只需要在 Controller 控制器的每个接口方法添加 @WebLog 注解即可。如果我们不想某个接口打印出入参日志,可以不加注解。
六、文件上传是否有效?
对于文件上传,不论是单文件上传还是多文件上传,切面日志都运行良好。有兴趣的小伙伴可以尝试一下。
七、如何在开发环境和测试环境中使用?
对于性能要求较高的应用,我们可以在开发环境或测试环境中使用,而不在生产环境中打印日志。我们只需为切面添加 @Profile 即可。
八、如何指定多切面的优先级?
如果我们服务中定义了多个切面,比如针对 Web 层接口,我们不仅想要打印日志,还要校验 token 等。我们可以通过 @Order(i) 注解来指定优先级。i 值越小,优先级越高。
面试官:你讲讲AOP与OOP有什么区别?
AOP全称为Aspect Oriented Programming,是一种面向切面编程的模式。它与传统的面向对象编程(OOP)有本质的区别。OOP主要关注的是对象的行为和属性,通过封装、继承和多态等特性来实现代码的复用和模块化。而AOP则更侧重于关注点的分离,它可以将一些横跨业务逻辑的公共行为或职责抽取出来,形成独立的模块,从而降低代码的耦合度,提高代码的可维护性和可扩展性。
AOP的核心思想是将业务逻辑中的横切关注点(如日志记录、性能监控、事务管理等)从核心业务代码中分离出来,通过预编译或运行时动态代理的方式进行统一管理和维护。这使得在修改或扩展业务逻辑时,不必修改原有代码,只需要在AOP的配置中添加或修改切点即可。这种分离关注点的设计模式有助于提高开发效率,降低维护成本。
AOP的应用场景通常包括但不限于日志记录、性能统计、安全控制、事务处理、异常处理等。例如,在一个APP模块结构中,按照OOP思想划分的“视图交互”、“业务逻辑”、“网络”等模块,若需要对所有模块的每个方法的执行时间进行监控,这正是AOP的典型应用场景。通过AOP,可以将监控逻辑与业务逻辑分离,无需在每个方法中重复实现监控代码,从而简化了代码结构,提高了代码的可维护性。
AOP的实现方式主要有运行时、加载时和编译时三种,其中编译时实现(如AspectJ)是AOP技术中最常用的一种,它通过在编译阶段将切面代码编织到目标代码中,实现了对目标代码的动态增强。
AspectJ是Java中的AOP实现,它包含两个核心组件:ajc编译器和weaver织入器。ajc编译器用于编译AspectJ的源代码,weaver则在编译或运行时将切面代码编织到目标代码中。在Android项目中使用AspectJ时,可以借助gradle插件来简化配置和集成过程。
总之,AOP提供了一种更为灵活、高效的方式来管理程序中的关注点,与OOP相比,它更加专注于解耦和提高代码的可维护性。在实际开发中,合理运用AOP可以显著提升软件开发的效率和质量。