目录
1 AOP 的思想
2 AOP 的组成
2.1 切面
3 AOP 的实现
3.1 添加 Spring AOP 依赖
3.2 定义切面
3.3 定义切点
3.4 实现通知
4 AOP 实现的一个例子
机场里有多家公司,但是安检是进候机室之前统一安排的,不需要不同航空公司各自安排一次安检。在计算机领域,也有类似的思想——AOP。
AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理。⽐如⽤户登录权限的效验,没学 AOP 之前,我们所有需要判断⽤户登录的⻚⾯(比如博客系统,所有需要用户登录的页面),都要各⾃实现或调⽤⽤户验证的⽅法,然⽽有了 AOP 之后,我们只需要在某⼀处配置⼀下,所有需要判断⽤户登录⻚⾯就全部可以实现⽤户登录验证了,不再需要每个页面都写相同的⽤户登录验证了。
AOP 是⼀种思想,⽽ Spring AOP 是⼀个框架,实现了 AOP 思想,两者之间的关系很像 MVC 与 Spring MVC 。
切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成。
切面:定义事件,也就是规定 AOP 要处理什么。比如用户登录效验
切点:定义具体规则,比如用户登录的拦截规则
通知:AOP 执行的具体方法,比如获取用户登录信息,如果已获取到说明已经登录了,否则就是未登录状态。
三者之间的关系类似于,公司的老板决定公司的业务是什么,而中层则来制定如何做事情的规则,最后由底层的劳动人民来实施具体的工作。
引入 Spring AOP 依赖:
org.springframework.boot
spring-boot-starter-aop
切面是一个类。需要两个注解。一个是 @Aspect ,表明该类是一个切面。
要让切面随着项目的启动而启动,因此要添加六大注解之一。这样才不会让那些在切面中定义的切点等方法,失去用处。
@Aspect //表明这个类是个 切面
@Component // 需要六大注解之一,以便在程序启动的时候便启动
public class UserAspect {
......
}
切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:
execution(<修饰符><返回类型><包名+类名+方法(参数)><异常>)
修饰符和异常通常可以省略。* 表示任意。
拦截 包名+类名 的所有方法的返回值
// 切点
// 包名 + 类名
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){
}
通知的方法:
前置通知使⽤@Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。 后置通知使⽤
@After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。 返回之后通知使⽤@AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。 抛异常后通知使⽤@AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。 环绕通知使⽤
@Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执 ⾏⾃定义的⾏为。
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getuser")
public String getUser(){
System.out.println("do getUser");
return "get user";
}
}
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/art")
public class ArticleController {
@RequestMapping("/getart")
public String getArticle(){
System.out.println("do getArticle");
return "getArticle";
}
}
package com.example.demo.common;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Aspect //表明这个类是个 切面
@Component // 需要六大注解之一,以便在程序启动的时候便启动
public class UserAspect {
// 切点
// 包名 + 类名
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){
}
// 前置通知
// 需要指定切点的方法名,针对那个切点做出的具体方法实现。
@Before("pointcut()")
public void doBefore(){
System.out.println("执行了前置通知");
}
// 后置通知
@After("pointcut()")
public void doAfter(){
System.out.println("执行了后置通知");
}
// 环绕通知
// 接受的参数类型一定是 ProceedingJoinPoint ,写死的
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知执行之前");
// 执行目标方法
Object result = joinPoint.proceed();
System.out.println("环绕通知执行之后");
return result;
}
}