前言
AOP即面向切面编程,是一种编程思想,作为面向对象思想的补充,目的是方便增强方法,处理一些具有横切性质的服务,比如日志记录,安全控制等,强大的Spring框架中亦运用了这种思想,那在探究了Spring实现AOP过程之后,在这里记录下自己的理解;
一、AOP是什么?
理解AOP之前,我们先回顾一下OOP(面向对象编程),众所周知,OOP的特点是继承、封装、多态,其中封装的一个目的就是把方法与类进行松耦合,在使用的时候,不同的类可以方便的调用各自的功能,说白了就是代码分散,增加代码重用性,降低代码复杂度。但是当代码分散的时候,也带来一个问题,打个比方,假如现在有两个类都要使用同一个日志打印方法,那根据OOP的思想,我需要在两个类中调用这个方法,但是注意这个独立的方法是在一个独立的类中,这样调用类和方法类就有了明显的耦合关系,独立方法类的改变会影响到调用类,那么有什么办法可以随意的加入代码的,那AOP就是做这样的事情,在程序预编译或者动态运行时,把公共操作切入类的指定方方法和指定位置处,这个就是面向切面编程(AOP),用一张图来表示AOP的思想:
如图所示,前者在不同的业务线中都插入了验证用户的代码,后者是把验证用户的代码,横向的切入到业务中(为了展示切入过程,放大了线条,实质上这种切入是不用修改业务的原来的代码的),Spring框架就具有这种能力,其关键就是运用了代理模式,在之前的文章对代理模式有专门介绍:一文搞懂代理模式
二、个人理解
理解了代理模式之后,对SpringAOP的理解从两个方面入手:
2.1 配置
本节介绍spring中关于AOP的配置以及涉及的常见AOP相关术语
1.常见AOP术语
- JoinPoint(连接点):指的是那些被拦截的点,在spring中这些点就是一个一个的方法。
- PointCut(切入点):指的是我们定义的想要拦截的点
注意: 连接点相当于菜单(spring中所有方法都可以是连接点),切入点才是真正的我们想点的菜
- Advice(通知):通知就是指作用到切入点上的具体方法,分为前置通知(Before)、后置通知(After)、环绕通知(Around)等。
- Aspect(切面或者方面):切面就是多个切点和通知的集合。
- Target(目标对象):代理的目标对象(要增强的类)
- Weaving(织入):把切面应用到目标对象来创建新的代理对象的过程就叫织入。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
- Proxy(代理):目标类被增强以后,会生成结果代理类。
2.Spring中关于AOP的配置
这里直接上示例:
创建一个maven类型的spring项目,这里就不赘述了,直接上pom.xml中的依赖引入部分
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
</dependencies>
创建一个带有@Aspect注解的方面类,这个就是Spring中关于Aspect的具体表现形式:
@Aspect
//方面类(用 @Aspect 修饰的类)和其他类一样可以
// 有方法、属性定义,还可能包括切入点、增强处理定义。
public class LogAspects {
……
}
填充切面类:
@Aspect
//方面类(用 @Aspect 修饰的类)和其他类一样可以
// 有方法、属性定义,还可能包括切入点、增强处理定义。
public class LogAspects {
//连接点就默认为项目中所有方法了
//设置切点
@Pointcut("execution(public int com.deee.springaop.MathCalculator.*(..))")
public void pointCut(){
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(" "+joinPoint.getSignature().getName()+"运行。。@Before的参数列表是:{"+Arrays.asList(args)+"}")