springboot面向切面编程
目录
什么是面向切面编程
- 全称Aspect Oriented Programming,面向切面编程,简称aop。
- 通俗理解是这样,我们有很多方法都要做一件相同的事情,我们可以把这件事情单独拿出来写个方法,通过aop方式,我们可以让指定的
方法去使用我们单独拿出来的这个方法。通常项目中,操作日志就是通过aop实现的。
基本概念
- @Aspect:作用在类上,代表这个类是一个切面配置类
通知的方式
- @Before:前置通知,作用在方法上,执行目标方法之前会进入前置通知
- @After:后置通知,作用在方法上,执行目标方法之后会进入后置通知
- @Around:环绕通知,作用在方法上,执行目标方法之前会进入环绕通知,环绕通知放行之后会执行目标方法,目标方法执行完之后会再进入环绕通知
切入点表达式
3个通知都有value属性,可以自定义切入点表达式,我们主要使用execution和within
- execution:方法级别,精确到每个方法
- within:类级别,精确到类,效率高。如果我们要作用在整个类上,就用within
::: warning 注意
推荐使用within,效率高,并且切入点表达式好写,我们只要指向某些类就行了。本节不讲具体的表达式如何写,想要了解更多内容请自行了解。
:::
顺序控制
@order:可以用在类上,也可以用在方法上,value属性,数字越小越先执行(经测试,加在方法上无效)
获取执行方法对象
在 @Before和@After方法里面,我们使用JoinPoint,在@Around方法里面,我们使用ProceedingJoinPoint
System.out.println("当前执行的方法名:"+joinPoint.getSignature().getName());
System.out.println("当前执行的方法所在类的全包名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前的目标对象:"+joinPoint.getTarget());
System.out.println("当前参数对象(是一个数组):"+joinPoint.getArgs());
::: tip 提示
注解里面的value属性可以省略,直接写值,比如@Before(value = "within(com.moyundong.controller.*Controller)")
可以写成@Before("within(com.moyundong.controller.*Controller)")
,@Order(value = 1)
可以写成@Order(1)
:::
如何使用
- 添加依赖
<!--开启aop所需的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建类MyAspect1,
- 2.1类上添加@Aspect、@Component注解,
- 2.2创建3个测试方法,分别对应@Before、@After和@Around
- 2.3切入点表达式
value = "within(com.moyundong.controller.*Controller)"
表示com.moyundong.controller包下所有以Controller
结尾的类里面的所有方法都受影响。 - 使用JoinPoint或者ProceedingJoinPoint获取了当前执行方法的相关信息。
@Aspect
@Component
@Order(value = 1)
public class MyAspect1 {
@Before(value = "within(com.moyundong.controller.*Controller)")
public void afterTest(JoinPoint joinPoint){
System.out.println("************前置通知MyAspect1*****************");
System.out.println("当前执行的方法名:"+joinPoint.getSignature().getName());
System.out.println("当前执行的方法所在类的全包名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前的目标对象:"+joinPoint.getTarget());
System.out.println("当前参数对象(是一个数组):"+joinPoint.getArgs());
}
@After(value = "within(com.moyundong.controller.*Controller)")
public void beforTest(JoinPoint joinPoint){
System.out.println("************后置通知MyAspect1*****************");
System.out.println("当前执行的方法名:"+joinPoint.getSignature().getName());
System.out.println("当前执行的方法所在类的全包名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前的目标对象:"+joinPoint.getTarget());
System.out.println("当前参数对象(是一个数组):"+joinPoint.getArgs());
}
@Around(value = "within(com.moyundong.controller.*Controller)")
public Object arountTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("*****************在目标方法执行前,进入环绕通知MyAspect1***************************");
System.out.println("当前执行的方法名:"+proceedingJoinPoint.getSignature().getName());
System.out.println("当前执行的方法所在类的全包名:"+proceedingJoinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前的目标对象:"+proceedingJoinPoint.getTarget());
System.out.println("当前参数对象(是一个数组):"+proceedingJoinPoint.getArgs());
try{
//执行目标方法,并且得到返回值
Object object = proceedingJoinPoint.proceed();
System.out.println("*****************在目标方法执行后,再进入环绕通知MyAspect1***************************");
//返回目标方法执行后的值
return object;
} catch (Throwable e){
System.out.println("*****************在目标方法执行出现异常时处理***************************");
return null;
}
}
}
测试
启动服务,在浏览器输入http://localhost:8088/moyundong/Test/hello
来看看效果。
大家可以再创建个MyAspect2,内容和MyAspect1差不多,@Order值为2,重新运行程序看看效果。把MyAspect1和MyAspect2的order顺序调换下,再执行下看看效果。