刚看到Spring AOP的时候,小编也是很是吃惊,在怀疑Spring AOP到底是啥??那么,欢迎对Spring AOP感兴趣的各位老铁更深入的探讨本文:
- 什么是AOP??
AOP(Aspect Oriented Programming):面向切面编程,它是一种思想,它是对某一类事情的集中处理,比如:用户登录权限的校验,没学AOP之前,我们所有需要判断用户登录的页面(中的方法),都要各自实现或调用用户验证的方法,然而有了AOP之后,我们只需要在某一处配置以下,所有需要批判的用户登录页面(中的方法),就全部可以实现用户登录验证了,不再需要每个方法都写相同的用户登录验证了,而AOP是一种思想,而Spring AOP是一个框架,提供了对AOP思想的实现,他们的关系和IoC与DI类似!
对于这种功能统一,且使用的地方较多的功能,就可以考虑AOP来统一处理了,除了统一用户登录判断之外,AOP还可以实现:
- 统一日志处理
- 统一方法执行时间处理
- 统一的返回格式设置
- 统一的异常处理
- 事务的开启和提交
- ..................
也就是说,使用AOP可以扩充多个对象的某个能力,所以AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
我们在AOP学习过程中,主要学习以下三个部分:
- 学习AOP是如何组成的??也就是学习AOP组成的相关概念
- 学习Spring AOP使用
- 学习Spring AOP实现原理
有了上述主要学习的三个方面,那么,来跟着小编思路来看一下吧!
AOP相关概念:
-
切面(Aspect)【类】:切面是AOP的核心概念,表示一个关注点的模块化,它跨越多个对象,包含了通知和切入点。切面可以理解为是在程序执行过程中特定的代码片段,它可以在程序的不同地方被执行,比如方法的前置或后置处理、异常处理等。
-
连接点(Join point):连接点是程序执行过程中可以插入切面的点,它表示某个特定的方法调用、异常抛出、变量赋值等程序执行的位置。
-
通知(Advice)【方法具体实现代码】:通知是切面具体的执行内容,即在连接点上执行的逻辑代码。通知包括了切面的具体操作,比如方法的前置、后置、环绕、异常等处理。
-
切入点(Pointcut):切入点是一个表达式,用于确定哪些连接点会被应用切面的通知。通过切入点可以定义切面将要发生作用的地方,以便于将切面应用到特定的方法调用上。
-
引入(Introduction):引入是一种动态地为类添加方法和属性的方式,可以让一个类在运行时实现额外的接口或者属性等。
-
织入(Weaving):织入是将切面应用到目标对象并创建新的代理对象的过程。织入可以是编译期、类加载期或者运行时进行。
看着上述内容很多,其实是由AI生成的!!(尴尬),那么,我们来看一下主要了解的相关概念吧!
- 切面【类】:指的是某一方面的具体内容就是一个切面,比如用户的登录判断就是一个"切面",而日志的统计记录它又是一个"切面";
- 切点【方法】:定义(一个)拦截器。
- 通知【方法具体实现代码】:执行AOP逻辑业务 a.前置通知(@Before):在目标方法(实际要执行的方法)调用之前执行的通知。
b.后置通知(@After):在目标方法调用之后执行的通知。
c.环绕通知(@Around):在目标方法调用前后都执行的通知(方法执行时间)。
d.异常通知(@AfterThrowing):在目标方法抛出异常的时候执行的通知(声明事务)。
e.返回通知(AfterReturning):在目标方法返回的时候执行的通知。
- 连接点:所有可能触发切点的点就叫连接点,比如:这里定义的切点是判断用户登录状态,此时我想去发布文章,发布文章就会触发去检查登录状态,这就是连接点。
有了上述些许知识储备,我们来看一下如何来实现Spring AOP吧!
实现Spring AOP:
-
添加Spring Boot AOP框架《------》Maven仓库(只能通过Maven仓库添加)
由于小编使用的是JDK8,因此选择了2.x.xx版本(适用于JDK8)
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.7.18</version> </dependency>
将上述的框架插入到IDEA中pom.xml文件中即可,然后刷新Maven下载依赖即可。
-
创建切面
//创建切面 @Aspect //切面 @Component //不能省略《------》随着项目启动而启动 public class UserAOP { }
-
创建切点:(定义拦截规则)
@Aspect //切面 @Component //不能省略《------》随着项目启动而启动 public class UserAOP { //切点(配置拦截规则) @Pointcut("execution(* com.example.demo.AOP.*(..))") public void pointcut(){ //空方法,不需要有实现,只是提供拦截规则。 } }
对于上述创建切点(配置拦截规则)的解析:
-
创建通知 以前置通知为列:
//前置通知 @Before("pointcut()") public void doBefore(){ System.out.println("执行了前置通知: "+ LocalDateTime.now()); }
注意,在这个代码中,pointcut()是个空方法
-
创建连接点(连接点:所有能触发切点的点)
@RestController public class UserController { @RequestMapping("user/sayHi") public String sayHi(){ System.out.println("执行了sayHi()方法"); return "hi spring boot aop"; } @RequestMapping("/user/login") public String login(){ System.out.println("执行了login()方法"); return "do user login"; } }
该段内容主要围绕前置通知来展开,但是,对于后置通知,返回通知《------》大致代码都差不多,主要还是注解的不同!所以,小编便不再进行展开!
但是,对于环绕通知则有稍微复杂性,那么,我们接下来便看一下环绕通知相关代码吧!!
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
//ProceedingJoinPoint 事件本身
System.out.println("开始执行环绕通知:");
Object obj=joinPoint.proceed(); //中间执行的方法
System.out.println("结束环绕通知~~");
return obj; //需要吧执行的结果还给框架,让框架继续zou后续的流程
}
经过上述的代码,我们来进一步了解Spring AOP的实现原理吧!!
Spring AOP的实现原理:
Spring AOP是构建在动态代理基础上,因此Spring对AOP的支持局限于方法级别的拦截。
Spring AOP支持JDK Proxy和CGLIB方式实现动态代理,默认情况下实现了接口的类,实现AOP会基于JDK生成代理类,没有实现接口的类,会基于CGBIB生成代理类。
Spring 的切面由包裹了目标对象的代理类实现,代理类处理方式的调用执行了额外的切面逻辑,并调用目标方法。
Spring AOP实现原理:JDK的动态代理和CGLIB动态代理实现的!!
JDK动态代理和CGLIB动态代理都是常见的动态代理实现技术,但是他们有以下区别:
- JDK动态代理基于接口,要求目标对象实现接口;CGLIB动态代理基于类,可以代理没有实现接口的目标对象。
- JDK动态代理使用java.long.reflect.Proxy和java.long.reflect.InvocationHandler来生成代理对象;CGLIB动态代理使用CGLIB库来生成代理对象。
- JDK动态代理生成的代理对象是目标对象的接口实现**;** CGLIB动态代理生成的代理对象是目标对象的子类。
- JDK动态代理性能相对较高,生成代理对象速度较快;CGLIB动态代理性能相对较低,生成代理对象速度较慢。
- JDK动态代理无法代理fianl类和final方法;CGLIB动态代理可以代理任意类的方法。
注:在Spring框架中,即使使用了JDK动态代理,又使用了CGLIB动态代理,默认情况下使用的是JDK动态代理,但是,如果目标对象没有实现接口,则会使用CGLIB动态代理。