AOP:面向切面编程,是一种思想,简单理解就是:针对于某一类通用的事情做集中的处理
SpringAOP:SpringAop是一个框架,提供了一种对 AOP思想的实现,它们的关系类似IoC和DI
AOP的常见使用
统一的用户登录判断,统一日志记录,统一方法执行时间统计,统一的返回格式设置,统一的异常处理,事务的开启和提交
AOP相关概念
1.切面(类):某一方面的具体内容就是一个切面,比如用户的登录判断就是一个切面,日志的统计记录也是一个切面
2.切点(方法):定义一个拦截规则
3.通知(方法具体实现代码):执行 AOP 逻辑任务
3.1前置通知: 在目标方法(实际要执行的方法) 调用之前执行的通知
3.2后置通知: 在目标方法调用之后执行的通知
3.3环绕通知: 在目标方法调用前后都执行的通知(例如方法执行时间的统计)
3.4异常通知: 在目标方法抛出异常时执行的通知
3.5返回通知: 在目标方法返回时执行的通知
4.连接点:所有有可能触发切点的事件
SpringAOC的使用
1.添加Spring AOP 框架
因为我们使用的是springboot项目,所以用的也是基于springboot的AOP框架
2.创建切面
在与启动类同级或者子集中创建一个类(切面),给这个类添加@Aspect注解和一个类注解可以是Component,也可以是Service等
@Aspect // 切面
@Component
public class UserAOP {
}
3.创建切点(定义拦截规则)
@Aspect // 切面
@Component
public class UserAOP {
// 切点
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
}
execution() 意思是执行execution括号里面的代码. *代表的是返回值,这里*号代表所有的返回值.com.example.demo.controller.UserController是包名+类名,意思是在demo底下的controller底下的UserController,所有返回值为*(所有的方法)都进行拦截. UserController后面的*表示具体拦截哪个方法(这里是*号所以表示任意)括号里的点点表示参数为任意
excution(<修饰符><返回类型><包.类.方法(参数)><异常>)
4.创建通知
@Aspect // 切面
@Component
public class UserAOP {
// 切点
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){}
// 前置通知
@Before("pointcut")
public void doBefore(){
System.out.println("执行了前置通知: "+ LocalDateTime.now());
}
}
5.创建连接点
controller底下的UserController类
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/user/sayHi")
public String sayHI(){
System.out.println("执行了sayHi方法");
return "hi springboot aop";
}
@RequestMapping("/user/login")
public String login(){
System.out.println("执行了login方法");
return "do user login";
}
}
controller底下的ArticleController类
@RestController
public class ArticleController {
@RequestMapping("/article/sayHi")
public String sayHI(){
System.out.println("执行了article sayHi方法");
return "hi article springboot aop";
}
}
依次在浏览器访问这三个方法
可以看到UserController类中的sayHi方法和login方法都执行了前置通知,而ArticleController类里面的sayHi方法没有执行前置通知,这是因为我们在创建切点,定义拦截规则的时候规定了的.
环绕通知
// 环绕通知
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开始执行环绕通知 ");
Object obj = joinPoint.proceed();
System.out.println("结束执行环绕通知");
return obj;
}
环绕通知需要把事件(正在执行的连接点)本身传递给环绕通知方法(ProceedingJoinPoint),然后在合适的位置调用ProceedingJoinPoint的proceed方法.最后再把执行的结果返回给框架
Spring AOP实现原理
Spring AOP底层实现:1.JDK 动态代理(通过反射实现,要求被代理的类一定要实现接口),性能较高
2.CGLIB(通过生成代理类的子类实现动态代理,要求被代理类不能被final修饰)