目录
AOP的概念
什么是AOP?
什么是SpringAOP?
为什要⽤ AOP?
AOP的作用?
AOP的组成
通知
AOP的实现
1. 添加 Spring AOP 框架⽀持。
2. 定义切面和切点。
3. 定义通知。
切点表达式
AOP的概念
什么是AOP?
AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的 集中处理。
什么是SpringAOP?
⽽ AOP 是⼀种思想,⽽ Spring AOP 是⼀个框架,提供了⼀种对 AOP 思想的实现,它们的关系和 IoC 与 DI 类似。
为什要⽤ AOP?
在开发中,我们对于公共方法的处理分为三个阶段:
- 每个方法都去实现(初级)
- 抽取公共方法(中级)
- 采用AOP的方式,对代码进行无侵入是实现(高级)
因此,对于这种功能统⼀,且使⽤的地⽅较多的功能,在之前我们会抽取公共方法去实现,但是现在有更好的一个办法来处理这个问题,就是AOP。
也就是使⽤ AOP 可以扩充多个对象的某个能⼒,所以 AOP 可以说是 OOP(Object Oriented Programming,⾯向对象编程)的补充和完善。
AOP的作用?
提供声明式事务;允许用户自定义切面
AOP的组成
AOP由下边四部分组成:
切面(Aspect) | 由切点(Pointcut)和通知(Advice)组成,既包含了横切逻辑的定义,也包含了连接点的定义。 |
连接点(Join Point) | 应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。 |
切点(Pointcut) | Pointcut的作用就是提供一组规则 来匹配Join Point,给满足规则的Join Point 添加Advice. |
通知(Advice) | 切面也有目标-他必须要完成的工作。在AOP中,切面的工作被称为通知。 |
那么是不是感觉难以理解呢?
不着急我们简单说一下, AOP使用来对某一类事情进行集中处理的,那么处理事情,这个整体就是一个切面,处理事情的范围就是切点,范围中具体的事物就是连接点,怎么处理就是通知。
简单的体系图如下:
通知
通知又名拦截器,它定义了切面是做什么以及何时使用,即在某个特定的连接点上执行的动作,它是切面的具体实现。以目标方法为参照点,根据放置位置的地方不同,通知分为如下5种类型通知:
- 前置通知使⽤ @Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
- 后置通知使⽤ @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
- 返回之后通知使⽤ @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
- 抛异常后通知使⽤ @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
- 环绕通知使⽤ @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为。
AOP的实现
实现步骤如下:
1. 添加 Spring AOP 框架⽀持。
2. 定义切⾯和切点。
3. 定义通知。
具体操作如下:
1. 添加 Spring AOP 框架⽀持。
首先我们建立一个新的Springboot项目,在配置文件中添加AOP框架:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义切面和切点。
3. 定义通知。
定义一个切面:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@Aspect
public class LoginAspect {
//定义切点
@Pointcut("execution(* com.example.springaopdemo.controller.UserController.*(..))")
public void pointcut(){}
//定义通知
@Before("pointcut()")
public void before(){
log.info("do before,,,,");
}
@After("pointcut()")
public void after(){
log.info("do after.....");
}
@AfterReturning("pointcut()")
public void afterReturning(){
log.info("do afterReturning....");
}
@AfterThrowing("pointcut()")
public void afterThrowing(){
log.info("do afterThrowing....");
}
//这里注意,环绕式通知必须自己写返回结果
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object oj=null;
log.info("之前");
try{
oj=joinPoint.proceed();//调用目标方法
}catch(Throwable e){
throw new RuntimeException(e);
}
log.info("之后");
return oj;
}
@Aspect是aop框架中提供的,表示是一个切面。切面类中最先定义的是切点,用@Pointcut注解表示是一个切点,括号里边是切点表达式。后边的是五大类型的通知。分别是用五大类名称进行注解。
切点表达式:
AspectJ ⽀持三种通配符
* | 匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数) |
.. | 匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使⽤。 |
+ | 表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如 com.cad.Car+ ,表示继承该类的 所有⼦类包括本身 |
切点表达式由切点函数组成,execution是最常用的切点函数,语法为:
修饰符和异常可以省略。
以上AOP部分的代码就已经完成,可以实现AOP的功能了,我们简单演示一下:
定义一个类来试一下它集中处理的实现:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/login")
public String login(){
log.info("login..");
return "login...";
}
@RequestMapping("set")
public String setup(){
log.info("setup...");
return "setup....";
}
@RequestMapping("/logout")
public String logout(){
log.info("logout...");
return "logout.....";
}
}
在浏览器执行过后就会发现执行结果:(任意执行一个方法,这里执行第三个方法)
可以看到五类通知的执行顺序如上,但是还有一个AfterThrowing没有看到打印,为什么呢?
这是因为AfterThrowing是在出现异常后才会进行打印的,在diamagnetic中插入一条异常代码,我们就会看到他的打印了:
以上代码能看出来了,AOP的确实一类事情的集中处理,且处理的时候不会破坏原有的代码,且允许用户自定义切面。