目录
机场里有多家公司,但是安检是进候机室之前统一安排的,不需要不同航空公司各自安排一次安检。在计算机领域,也有类似的思想——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;
- }
- }