文章目录
SpringAOP
Spring两大核心思想:IoC和AOP(AOP是面试频率最高的)
- AOP就是对某一类特定问题的集中处理 (AOP是一种思想),AOP是面向切面编程,OOP是面向对象编程

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

java
@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是先执行外圈,再执行内圈的,就和一个圆环是类似的
执行顺序:

- 接口响应异常

执行顺序:

java
// 主要代码
@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修饰的

在其他类中使用

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

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

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

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

通配符的作用:

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

自定义一个注解:
java
// 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原理是基于代理来实现的
代理模式:有静态代理和动态代理
什么是代理模式??
举个例子:
租户直接找房东就是没有代理模式的,
租户通过中介找房东就是有代理模式的

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

java
// 接口
package com.example.aopdemo.demos.proxy;
// 要实现的出租房子的接口
public interface HouseSubject{
// 出租房子
void rentHouse();
// 卖房子
void saleHouse();
}
package com.example.aopdemo.demos.proxy;
// 目标对象
// 房东
public class RealHouseSubject implements HouseSubject{
@Override
public void rentHouse() {
System.out.println("我是房东,我要出租房子!");
}
@Override
public 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;
}
@Override
public void rentHouse() {
System.out.println("开始进行代理");
houseSubject.rentHouse();
System.out.println("结束代理");
}
@Override
public 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 动态代理实现
添加依赖:
java
<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


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