点一下关注吧!!!非常感谢!!持续更新!!!
大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html
目前已经更新到了:
- MyBatis(已更完)
- Spring(正在更新…)
章节内容
上节我们完成了:
- 循环依赖 原型Bean
- 原型作用域 Lazy
- ObjectFactory
Spring AOP
AOP 的本质:在不改变原有逻辑的情况下,增强横切的逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。
Spring AOP(面向切面编程,Aspect-Oriented Programming)是Spring框架的一部分,提供了基于代理的AOP功能。它允许你在不改变原有代码的情况下,向应用程序中添加额外的功能或行为。Spring AOP通过切面(Aspect)、连接点(Joinpoint)、通知(Advice)和切点(Pointcut)等概念提供了对程序流的控制,帮助实现横切关注点的模块化。横切关注点指的是跨越多个类或模块的功能,比如日志记录、事务管理、安全控制等。
核心概念
切面(Aspect)
切面是AOP的核心概念,表示横切关注点的模块化。切面是由切点和通知组成的,切面定义了哪些方法会应用额外的功能(通知)。
连接点(Joinpoint)
连接点表示程序执行中的一个点,比如方法调用、方法执行、构造函数调用等。Spring AOP中的连接点通常是方法执行的地方。
切点(Pointcut)
切点定义了哪些连接点是需要拦截的,它通常是基于方法签名进行筛选的。例如,选择某个包下所有的方法,或者某个类的方法。切点是一个表达式,表示何时需要执行通知。
通知(Advice)
通知定义了在连接点上执行的代码,通知决定了增强的具体行为。通知有不同的类型,分别是:
- 前置通知(Before):方法执行前执行。
- 后置通知(After):方法执行后执行,不论方法是否抛出异常。
- 返回通知(AfterReturning):方法正常执行完后执行。
- 异常通知(AfterThrowing):方法抛出异常时执行。
- 环绕通知(Around):可以控制方法的执行,既可以选择执行方法,也可以选择不执行方法,或者修改方法的返回值。
- 目标对象(Target Object):
目标对象是被AOP代理的对象,它是切面操作的实际对象。
Spring AOP的工作原理
Spring AOP基于代理模式来实现,它主要通过两种方式生成代理对象:
- JDK动态代理:适用于目标对象实现了接口的情况。Spring会生成目标对象接口的代理类。
- CGLIB代理:适用于目标对象没有实现接口的情况。Spring会通过CGLIB库生成目标对象的子类作为代理。
Spring AOP的优缺点
优点
- 解耦:Spring AOP允许将横切关注点从核心业务逻辑中分离出来,增强了模块化。
- 透明性:通过AOP,开发人员可以不改变原有代码,增加新的功能。
- 灵活性:通过切面、通知、切点的灵活配置,可以精确控制横切功能的执行。
缺点
- 性能开销:因为每个代理对象都会拦截方法调用,可能会对性能产生影响,特别是在复杂的应用中。
- 复杂性:对于初学者来说,理解AOP的概念和Spring AOP的配置可能有一定的难度。
相关术语
业务主线
在讲解 AOP 术语之前,我们先来看一下这些图,它们的第三部分案例需求的扩展(针对这些扩展的需求,我们只进行分析,在这个基础上进一步去回顾 AOP)
上图描述的就是未采用的 AOP 思想的设计的程序,当我们红色框中圈定的方法时,会带来大量的重复劳动,程序中充斥着大量的重复代码,使我们程序的独立性很差,而下图中采用了 AOP 思想涉及的程序,它把红框部门的代码抽取出来的同时,运用动态代理技术,在运行期间对需要使用业务逻辑方法进行增强。
AOP术语
- JoinPoint 连接点:它指的是那些可以用于增强代码加入到业务主线中的点,那么由上图中我们可以看出,这些点指的就是方法。在方法执行的前后通过动态代理的技术加入增强的代码。在 Spring 的框架 AOP思想的技术实现中,也只支持方法类型的连接点。
- Pointcut 切点:它指的是已经增强的代码加入业务主线进行之后的连接点,由上图中,我们看出表现层 transfer 方法就只是连接点,因为判断访问权限的功能并没有对其进行增强。
- Advice 通知增强:它指的是切面中用于提供增强功能的方法,并且不同方法增强的时机是不一样的。比如,开启事务肯定是在要在业务方法执行之前的,提交事务要肯定在业务方法执行之后的,而回滚的执行需要在业务方法出现错误的时候再执行。这些就是通知类型,目前的分类有:前置通知、后置通知、异常通知、最终通知、环绕通知。
- Target 目标对象:它指的是代理的目标对象,即被代理对象
- Proxy 代理对象:它指的是一个类被 AOP 织入增强后,产生的代理类,即代理对象。
- Weaving 织入:它指的是增强应用到目标对象来创建新的代理的过程,Spring 采用动态代理织入,而 AspectJ 采用编译期间织入和类装载期间织入。
- Aspect 切面:它指定是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面,例如,事务切面,它里面定义的方法和事务相关的,像开启事务、提交事务、回滚事务等等,不会定义其他与事务无关的方法,之前提到的TransactionManager 就是一个切面。
这里还需要进行一定的解释:
- 连接点:方法开始时、结束时、正常运行完毕的时候、方法异常的时候等等,这些特殊的时间点,我们称做连接点,项目中每个方法都有连接点,连接点是一种候选点。
- 切入点:指定 AOP 思想要影响的具体的方法是哪些
- Advice 增强,第一个层次:指的是横切逻辑
- Adevice 增强,第二个层次:方位点(在某一些连接点上加入横切逻辑,那么这些连接点就叫做方位点,描述的是具体的特殊时机)
- Aspect:切面概念是对上述概念的一个综合解释,切面=切入点+增强
AOP 代理选择
Spring 实现 AOP 思想使用的动态代理技术,默认情况下,Spring 会根据被代理对象是否实现接口来选择JDK 还是 CGLIB,当被代理对象实现了接口,Spring 会选择 JDK 官方的代理技术,不过我们可以通过配置的方式,让 Spring 强制使用 CGLIB 的方式。
AOP 配置方式
在 Spring 的 AOP 的配置中,和 IoC 配置一样,支持三类的配置方式:
- 使用 XML 配置
- 使用 XML + 注解的方式配置
- 使用纯注解的方式配置
AOP 实现
需求描述
横切逻辑代码是打印日志,希望打印日志的逻辑织入到目标方法的特定位置
添加依赖
我们需要在 pom 中添加依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.1.12.RELEASE</version>
</dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version>
</dependency>
核心配置
我们在基于 XML 的配置步骤如下:
- 把通知的 Bean 交给 Spring 管理
- 使用 aop:config 开始 AOP 的配置
- 使用 aop:aspect 配置切面
- 使用对应的标签配置通知的类型,入门的案例采用前置通知,标签为 aop:before
<!-- 日志工具 交由给 Spring 进行管理 -->
<bean id="wzkLogUtils" class="wzk.utils.WzkLogUtils"></bean>
<!-- AOP 配置 -->
<aop:config><!-- 配置切面 --><aop:aspect id="wzkLogAdvice" ref="wzkLogUtils"><aop:before method="printLog" pointcut="execution(public * wzk.service.impl.WzkTransferServiceImpl.update(wzk.model.WzkAccount))"></aop:before></aop:aspect>
</aop:config>
对应的代码如下所示:
上述的配置实现了 WzkTransferServiceImpl.update 方法进行了增强,在其执行之前,输出了记录日志的语句,这里有一个新的概念:【切入点表达式】
切入点表达式
切入点表达式也叫做 AspectJ 切入点表达式,指的是遵循特定的语法结构的字符串,其作用的是用于对符合的语法格式的连接点进行增强,它是 AspectJ 的一部分。
AspectJ 是一个基于 Java 的 AOP 框架,Spring 框架从 2.0 版本之后集成了 AspectJ 框架中切入点表达式部分,开始支持 AspectJ 切入点表达式。
改变代理的配置
改变代理的配置,Spring 在选择创建代理的时候,会根据被代理对象实际情况来选择的,被代理对象实现了接口,则采用基于接口的动态代理,当被代理对象没有实现任何接口的时候,Spring 会自动切换到基于子类的动态代理方式。
但是我们都知道,无论被代理对象是否实现接口,只要不是 final 修饰的类都可以采用 CGLIB 提供的方式创建代理对象,所以 Spring 也考虑到了这个情况,提供了配置方式实现强制使用基于子类的动态代理(即 CGLIB 的方式),配置的方式有两种:
- aop:config 标签来配置 proxy-target-class=“true”
- aop:aspectj-autoproxy 标签来配置 proxy-target-class=“true”