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