Java SpringAOP --- AOP的使用,AOP的源码
文章目录
- SpringAOP
- 引入AOP依赖
- AOP的使用
- AOP的详解
- AOP概念(理解)
- 通知类型
- 切点
- 切面的优先级
- 切点表达式
- 注解匹配
- Spring 原理
- 代理模式
- 静态代理
- 动态代理
- AOP的源码(了解)
SpringAOP
Spring两大核心思想:IoC和AOP(AOP是面试频率最高的)
- AOP就是对某一类特定问题的集中处理(AOP是一种思想),AOP是面向切面编程,OOP是面向对象编程

引入AOP依赖
- 在pom.xml文件中添加配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP的使用
- 实现一个AOP的实例:打印所有接口的耗时时间,这就是对所有方法的统一处理

@Slf4j
@Aspect
@Component
public class TimeAspect {// ProceedingJoinPoint表示的作用目标方法是什么// 针对所有的方法都生效使用的注解@Around("execution(* com.example.demo.controller.*.*(..))")public Object timeCost(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();// 让作用的目标方法执行Object result = joinPoint.proceed();long end = System.currentTimeMillis();log.info(joinPoint.getSignature() + "消耗的时间: " + (end - start) + "ms");return result;}
}


AOP的详解

AOP概念(理解)
-
切点:一组规则,通过表达式来描述,告诉程序对哪些方法来进行功能增强。
切点就是要作用的路径
切点表达式:

-
连接点:切面要作用的方法,目标方法,切点要描述的方法
连接点就是作用的具体方法

-
通知:具体的逻辑,要做什么处理(可以理解为代码的逻辑)
-
切面:切点 + 通知
举个例子:
切点就是某个班的学生
连接点就是某个具体的学生,张三,李四
123班学生今天要进行javaee的考试,要进行的考试就是通知
AOP是对一类事情的集中处理

通知类型

- 接口响应正常

都是一圈一圈执行的,Around是先执行外圈,再执行内圈的,就和一个圆环是类似的
执行顺序:

- 接口响应异常

执行顺序:

// 主要代码@Slf4j
@Aspect
@Component
public class AspectDemo {// 第一个.*是所有的类都生效,第二个.*是所有的方法都生效@Before("execution(* com.example.aopdemo.demos.controller.*.*(..))")public void doBefore(){log.info("执行AspectDemo before...");}@After("execution(* com.example.aopdemo.demos.controller.*.*(..))")public void doAfter(){log.info("执行AspectDemo after...");}@AfterReturning("execution(* com.example.aopdemo.demos.controller.*.*(..))")public void doAfterReturning(){log.info("执行AspectDemo afterReturning...");}@AfterThrowing("execution(* com.example.aopdemo.demos.controller.*.*(..))")public void doAfterThrowing(){log.info("执行AspectDemo afterThrowing...");}@Around("execution(* com.example.aopdemo.demos.controller.*.*(..))")// Around必须返回结果public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("执行AspectDemo around 前...");// 调用目标方法,执行结果Object result = joinPoint.proceed();log.info("执行AspectDemo around 后...");return result;}
}// 测试代码
@RestController
public class HelloController {@RequestMapping("/helloo")public String hello(){return "hello";}@RequestMapping("/t1")public String t1(){int a = 10 / 0;return "t1";}
}
切点
每个方法前都要写这个切点,是不是太麻烦了??
有没有更简单的,可以把这行提取出来呢?

- 定义一个切点叫做pt,这个名字是可以任意取的

- 定义的这个切点能否在其他切面(类)中使用呢??
这个切点可以再其他类中使用的,如果其他类需要使用,需要把切点声明为public,使用时,类的全限定名称(包 + 类名) + 切点名称
切点名称为public修饰的

在其他类中使用

3. 当有多个切面时,切面的执行顺序是按名称进行排序的
优先级高:先执行before,后执行After

切面的优先级
- 如果开发的时候,要算切面的执行顺序是怎么样的,实在是太麻烦了??
使用@Order来定义切面的优先级

从图中可以看出,先执行优先级高的before方法,再执行优先级低的before方法,再执行目标方法,再执行优先级低的after方法,最后执行优先级高的after方法

切点表达式
- 切点表达式的类型以及其写法:
execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

通配符的作用:

下面这些了解就可以了
TestController 下的 public修饰, 返回类型为String 方法名为t1, 无参方法
execution(public String com.example.demo.controller.TestController.t1())
省略访问修饰符
execution(String com.example.demo.controller.TestController.t1())
匹配所有返回类型
execution(* com.example.demo.controller.TestController.t1())
匹配TestController 下的所有无参方法
execution(* com.example.demo.controller.TestController.*())
匹配TestController 下的所有方法
execution(* com.example.demo.controller.TestController.*(..))
匹配controller包下所有的类的所有方法
execution(* com.example.demo.controller.*.*(..))
匹配所有包下面的TestController
execution(* com..TestController.*(..))
匹配com.example.demo包下, 子孙包下的所有类的所有方法
execution(* com.example.demo..*(..))
注解匹配
为了解决要包含这个类的所有方法,所有的类,这种就太宽泛了,为了指定特定的路径或者是方法使用注解匹配
- 编写自定义的注解
- 使用@annotation表达式来描述切点
- 在连接点的方法上添加自定义注解
annotation就是注解的意思
自定义注解作用就是指定哪些方法使用,内部的一些内容

自定义一个注解:
// METHOD表示只能作用到方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MyAspect {}
(1) @Tatget作用的范围

(2) 注解的生命周期

常见的面试题:

Spring 原理
- AOP常见的实现方式:
aspectJ自己也有实现AOP的方式,但是我们只用学Spring实现AOP的方式,虽然是Spring用了aspectJ的名字,但是也是自己实现的

代理模式
- Spring AOP原理是基于代理来实现的
代理模式:有静态代理和动态代理
什么是代理模式??
举个例子:
租户直接找房东就是没有代理模式的,
租户通过中介找房东就是有代理模式的

静态代理
- 静态代理:在程序运行前,代理对象就已经对目标对象进行了步骤的预执行代码
举个例子:

// 接口
package com.example.aopdemo.demos.proxy;// 要实现的出租房子的接口
public interface HouseSubject{// 出租房子void rentHouse();// 卖房子void saleHouse();
}package com.example.aopdemo.demos.proxy;// 目标对象
// 房东
public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东,我要出租房子!");}@Overridepublic void saleHouse() {System.out.println("我是房东,我要卖房子!");}
}// 代理对象
package com.example.aopdemo.demos.proxy;// 中介是没有房子的,最后还是需要房东来做这个工作的
public class HouseProxy implements HouseSubject{// 目标对象,被代理对象private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;}@Overridepublic void rentHouse() {System.out.println("开始进行代理");houseSubject.rentHouse();System.out.println("结束代理");}@Overridepublic void saleHouse() {System.out.println("开始进行代理");houseSubject.saleHouse();System.out.println("结束代理");}
}// 测试类
package com.example.aopdemo.demos.proxy;public class Main {public static void main(String[] args) {// 目标对象// 静态代理:在代理的时候,就知道了是哪个对象HouseSubject subject = new RealHouseSubject();HouseSubject proxy = new HouseProxy(subject);proxy.rentHouse();}
}
- 代理模式的主要角色:
proxy就是代理的意思,以后在读源码的时候看到这个就翻译为代理

动态代理
- 动态代理常见的实现方式:
(1) JDK
(2) CGlib:这是第三方提供的
(1) JDK
动态代理和静态代理只需要理解就行,不需要自己写代码
JDK只能代理接口,不能代理类,这是规定

(2) CGLIB 动态代理实现
添加依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
通过反射进行实现:

代理的目的:对目标对象进行功能的增强
CGLIB 既可以代理类,又可以代理接口
AOP的源码(了解)
容易考面试题:
SpringAOP 的实现方式:
(1) execution
(2) 自定义注解
-
Spring AOP是怎么实现的
是基于动态代理实现的 -
动态代理是怎么实现的
Spring 动态代理是基于JDK和CGlib实现的 -
Spring 使用的是哪个?
两个都有 -
什么时候使用JDK,什么时候使用CGLIB??
那么代理接口的时候,什么时候使用JDK,什么时候使用CGLIB??
在SpringBoot 2.X之前,类是使用cglib代理,接口是使用jdk代理

-
JDK 和 CGLib有什么区别??
代理类,只能使用CGlib
代理接口,可以使用JDK,也可以使用CGLIB


设计模式考的是思想,而不是代码
最容易问的模式:
策略模式和模版模式的区别,
代理模式和适配器模式的区别,
最有可能写代码的是单例模式

