文章目录
一、什么是 AOP 、Spring AOP
1.1 什么是AOP
- 地位:Spring两大核心之一,另一个是loC
- 什么是AOP:AOP和OOP主要是处理事情的维度不同,都是一种思想,实现方法有很多种
- AOP:面向切面编程(对某一类特定的问题进行编程)
- 什么是切面:某一类特定问题
- 关于统一功能:
- 统一功能处理就是AOP的一个实现,因为拦截器、统一异常等问题就是一个【特定的问题】
- 也因为只是其中一个实现,统一功能的代码并没有用到@Before、@After之类的注解
- AOP常见的实现方式:Spring AOP、aspectJ(Spring使用了aspectJ的注解【@Aspect】,但自己进行了实现)
- OOP:面向对象编程(在进行Java编程时,把图书、人之类的想成一个类)
- AOP:面向切面编程(对某一类特定的问题进行编程)
- AOP优点:不修改原始的业务代码就能增加新的功能,提高了开发效率
1.2 什么是Spring AOP
- 什么是Spring AOP:AOP是一种思想,实现方式有很多,Spring也实现了AOP,我们把其称为【Spring AOP】
- Spring AOP有什么用:可以自定义AOP
- 为什么需要自定义:有些需求无法被Spring写好的的AOP实例满足
- Spring针对一些场景的场景,帮我们写了AOP也就是【统一功能】。但就像拦截器的作用维度是URL一样,有时候现有的AOP没有办法完成一些定制化的需求,此时我们就可以借助Spring给我们提供的API,自定义AOP
- 为什么需要自定义:有些需求无法被Spring写好的的AOP实例满足
二、AOP概念
2.1 切点
- 什么是切点:表示一组规则,告诉程序目标方法是什么,通过切点表达式来描述
- 如何把切点提出来定义:
- 如何在其他切面类中使用这个提出来的切点:
2.2 切点表达式
- 什么切点表达式:如【execution(* com.example.book_test.controller..(…))】
- 第一个.*:controller包下的所有类
- 第二个*:该类下的每一个方法
- 切点表达式的两种表达方式:匹配得上就执行,匹配不上就不执行
-
execution)(……):通过方法的签名来匹配,更加倾向于具有一定规则的
-
@annotation:根据注解匹配,当规则比较难找/难描述的时候,就可以使用注解匹配
- 实现步骤:
【1】编写自定义注解
【2】使用@annotation表达式来描述切点
【3】在连接点的方法上添加自定义注解 - 代码实现
- 实现步骤:
-
2.2 连接点
- 什么是连接点:切点描述的方法、切面要作用的方法、目标方法,具象为【ProceedingJoinPoint joinPoint】
2.3 通知
- 什么是通知:对要处理的一类问题的处理逻辑
- 通知类型:注意@Around注解必须要有返回值,其他注解无所谓。如果@Around没有返回结果,那整个方法也没有结果(返回值是joinPoint.proceed()的结果)
- @Around:环绕通知,该注解标注的通知方法在目标方法前后都被执行
- @Before:前置通知,该注解标注的通知方法在目标方法前被执行
- @After:后置通知,该注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning:返回后通知,该注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing:异常后通知,该注解标注的通知方法在发生异常后执行
- 不同通知类型的执行顺序:
2.4 切面
- 什么是切面:切点 + 通知
- 切面优先级:
-
为什么会有切面优先级:
-
原理:
-
如何定义切面优先级:
-
三、代码案例:实现打印所有接口执行时间的AOP
- 不用AOP,如何打印一个接口的执行时间:
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
System.out.println("执行时间:" + (end - start) + "ms");
- 使用AOP操作:
- 为什么可以用AOP:【需要打印每个接口的执行时间】就是一类问题
- 代码:
- @Aspect:表明这是一个切面类,该注解来自于引入的AOP依赖,但此处只是应用了Aspect(一个第三方包)的注解,真正的实现还是在Spring这里
- import org.aspectj.lang.annotation.Aspect; 使用了该包里的@Aspect注解,实现不是它
- @Around:环绕着目标方法执行
- “execution(* com.example.book_test.controller..(…))”:用表达式来指定哪些是目标方法
- 该表达式指【对所有在controller层里的代码生效】
- ProceedingJoinPoint joinPoint:当前作用到的目标方法
- @Aspect:表明这是一个切面类,该注解来自于引入的AOP依赖,但此处只是应用了Aspect(一个第三方包)的注解,真正的实现还是在Spring这里
四、Spring AOP 是如何实现的
Spring AOP是基于动态代理来实现的
4.1 代理模式
- 分类:分为静态代理模式和动态代理模式
- 生活中的代理:
- 如果有人要联系一个公司的老板,需要先由秘书进行代理,由秘书与老板进行沟通
- 有人要买房子,卖方把房屋授权给中介,由中介来代理买房事宜
- 代理的作用:可以在业务实现类提供的功能基础上,增强额外的功能
- 比如房东可以租房子给我们,但如果是遇到维修之类的问题,房东不会处理,因为房东提供的功能是有限的。而如果有中介这个代理,中介可以在房东提供的功能的基础上,对功能进行增强,比如在房东真的租房前,给我们介绍房子的相关信息,在租房后,给我们进行一些售后服务
- 代理的效果:
- 适配器模式 VS 代理模式:适配器模式解决接口不兼容的问题,代理模式则是控制对其他对象的访问
- 设计模式最根本的区别是设计思想,而非代码实现上的差别
4.2 静态代理
- 什么是静态代理:在程序运行前,代理对象就已经对目标对象进行了步骤的预执行代码(通过编译),知道将要执行哪些操作,运行哪些代码
- 静态:不变了
- 举例:打扫卫生前,提前安排出一张值日表,在还没有正式打扫前,每个人都知道自己要负责的职位
- 代码实现:代码的逻辑都是提前写好的,在编译运行之后,谁去代理谁,去执行什么操作都是确定好了的
4.3 动态代理
【1】概念
- 什么是动态代理:提前没有代理对象,程序在运行过程中,才会去临时生成一个代理对象并进行代理操作(不需要提前去预知有哪些工作要去做,而是当事情来了之后,再去处理)
- 为什么要有动态代理:静态代理时,每修改一下业务接口,业务实现类和代理类都需要发生变更,十分繁琐
- 举例:等到开始打扫时,发现哪块比较脏,就临时安排一个空闲的人去打扫
- 常见的两种动态代理的实现方式:Java也对动态代理进行了实现,并提供了一些API,下面是两种常见的实现方式
- JDK动态代理:可以代理接口,不能代理类
- CGLIB动态代理:CGLIB是一个第三方的包。类和接口都能代理
【2】JDK动态代理
- 实现步骤:
- 定义一个业务接口及其业务实现类
- 实现InvocationHandler接口,并重写involve方法(这里面)
- 通过 【Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)】创建代理对象
- 代码实现:
- JDK动态代理的缺点:
【3】CGLIB动态代理
- 实现步骤:
- 定义一个被代理类
- 自定义MethodInterceptor,并重写intercept方法(用于增强目标方法,类似于JDK动态代理中的invoke方法)
- 通过Enhancer类的 create() 创建代理类
- 代码编写:
- 可代理对象:类和接口都可以代理
4.4 用原理讲解Spring AOP
4.5 源码分析