一、AOP概述
1、什么是AOP
AOP(Aspect Oriented Programming):面向切片编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高 了开发的效率。
来源百度:https://baike.baidu.com/item/AOP/1332219?fr=aladdin
SpringAOP是一种约定流程的编程,利用AOP可以将代码织入事先约定的流程中,在你项目原有的基础功能上,可以通过AOP去添加新的功能,AOP 有助于我们将不同但是有必要的重复性代码重构为不同的模块。这样我们可以将这些重复性代码集中管理起来复用,而不是每次都要重复写一遍。
举例如下:
当要对一个项目进行日志管理的时候,往往会在每个方法下添加日志处理的方法:
由于日志处理的代码几乎相同,但我们仍需手动去插入这些方法,且当我们不需要用这个处理方法时,或者想换成其他方法时,就需要去手动修改,这给我们实际编程带来了很多不便。
而利用AOP,可以解决这种问题,只需要通过动态代理,在指定的位置执行相应的流程。利用这种方法,可以将一些功能模块抽离出来形成一个独立的模块,然后在指定的位置插入这些模块:
2、AOP术语
- **Join Point(连接点):**连接点对于的是被拦截的对象,因为Spring只能支持方法,因此被拦截的对象往往是指定的方法,而连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时,抛出异常时,甚至修改一个字段时,切面代码就会将插入到应用的正常流程之中,并添加新的行为。
- **Point Cut(切点):**切点定义了在哪个地方应用通知(advice)。通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
- **Advice(通知):**通知表示在切面上某个切入点要执行的动作,其中包括了“around”、“before”和“after”等不同类型的通知:
类型 | 作用 |
---|---|
前置通知(Before) | 在目标方法被调用之前调用通知功能 |
后置通知(After) | 在目标方法完成之后调用通知,不关心方法的输出是什么。是“返回通知”和“异常通知”的并集。 |
返回通知(After-returning) | 在目标方法成功执行之后调用通知 |
异常通知(After-throwing) | 在目标方法抛出异常后调用通知 |
环绕通知(Around) | 通知包裹了被通知的方法,可同时定义前置通知和后置通知。 |
- **Target(目标对象):**被通知的对象。也就是需要加入额外代码的对象,也就是真正的业务逻辑被组织织入切面。
- **Introduce(引入):**引入让一个切面可以声明被通知的对象实现了任何他们没有真正实现的额外接口,而且为这些对象提供接口的实现。
- **Waveing(织入):**织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以织入。
- **Aspect(切面):**其实就是通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行。
二、SpringBoot整合AOP
1、创建SpringBoot项目,导入Web以及AOP依赖:
<!-- Web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、创建一个Controller对象,用来处理业务逻辑:
@RestController
public class TestController {
@RequestMapping("test01")
public String Test01(){
System.out.println("Test01............");
return "Success";
}
@RequestMapping("test02")
public String Test02(){
System.out.println("Test02............");
return "Success";
}
@RequestMapping("test03")
public String Test03(){
System.out.println("Test03............");
return "Success";
}
@RequestMapping("test04")
public String Test04(){
System.out.println("Test04............");
return "Success";
}
}
在访问localhost:8080/test01会返回:
控制台也会输出对应信息:
3、插入切点和通知:
//@Aspect注解标识了该类为切面类,@Component注解将这个类注入到IOC容器中
@Aspect
@Component
public class TestAOP {
// 定义切入点,切点为per.controller.TestController中的所有函数
@Pointcut("execution(public * per.controller.TestController.*(..)))")
public void pointCut(){
}
// 定义在连接点执行之前执行的通知
@Before("pointCut()")
public void doBefore(){
System.out.println("Before.............");
}
// 定义在连接点执行之后执行的通知
@After("pointCut()")
public void doAfter(){
System.out.println("After.............");
}
}
执行结果如下:
1、访问localhost:8080/test01,客户端正常返回,在控制台可以看到执行了通知:
由于定义了切入点为TestController下的所有函数,因此在访问其他页面时也会有通知: