SpringAOP

目录

快速入门

引入AOP依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写简单的代码:实现一个统计方法执行的时间

java 复制代码
@Slf4j
@Aspect
@Component
public class TimeRecordAspect {
    @Around("execution(* com.wpre.springaop.controller.*.*(..))")
    public Object timeRecord(ProceedingJoinPoint pjt) throws Throwable {
        //记录开始时间
        long start = System.currentTimeMillis();

        //执行目标方法
        Object ret = pjt.proceed();

        //记录结束时间
        long end = System.currentTimeMillis();

        log.info("{}耗时:{}ms", pjt.getSignature(), end - start);

        return ret;
    }
}

切点

切点(PointCut),也称切入点,是一个表达式,用于匹配哪些类、方法(或字段)是通知(Advice)应该被应用的地方

切面

切面是实现横切关注点的模块化单位。它由两个主要部分组成:切点和通知。切面将切点和通知结合起来,定义了在哪些连接点上应用哪些通知。切面通常用类和注解来表示。

通知

通知是切面的一部分,它定义了在切点匹配到的连接点上应该执行的逻辑。通知可以在方法执行之前、之后或抛出异常时被触发

连接点

连接点是程序执行过程中的特定点,这些点可以被Spring AOP框架所拦截。在Spring AOP中,连接点通常是方法的执行,但也包括异常抛出等其他类型的连接点

通知类型

  • 这是通知的具体分类,每种类型的通知定义了在程序执行的不同阶段执行的逻辑。具体包括:
    • Before Advice:在目标方法执行之前执行的通知。
    • After Returning Advice:在目标方法成功执行后执行的通知。
    • After Throwing Advice:在目标方法抛出异常后执行的通知。
    • After (Finally) Advice:无论目标方法是否抛出异常,都会在方法执行后执行的通知。
    • Around Advice:在目标方法执行前后都能执行的通知,可以控制方法的执行流程。

代码案例:

java 复制代码
@Slf4j
@Aspect
@Component
public class AspectDemo {
    @Before("execution(* com.wpre.springaop.controller.*.*(..))")
    public void doBefore() {
        log.info("AspectDemo 执行 doBefore");
    }

    @After("execution(* com.wpre.springaop.controller.*.*(..))")
    public void doAfter() {
        log.info("AspectDemo 执行 doAfter");
    }

    @AfterThrowing("execution(* com.wpre.springaop.controller.*.*(..))")
    public void doAfterThrowing() {
        log.info("AspectDemo 执行 doAfterThrowing");
    }

    @AfterReturning("execution(* com.wpre.springaop.controller.*.*(..))")
    public void doAfterReturning() {
        log.info("AspectDemo 执行 doAfterReturning");
    }

    @Around("execution(* com.wpre.springaop.controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint pjt) throws Throwable {
        log.info("AspectDemo 执行 doAround前");
        Object proceed = pjt.proceed();
        log.info("AspectDemo 执行 doAround后");
        return proceed;
    }

}

切点表达式

常见的切点表达式有两种:

1、execution(...):根据方法签名匹配

2、@annotation(...):根据注解匹配

execution表达式

语法:

tex 复制代码
execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)

例如

1、TestController下的public修饰,返回类型为String,方法名为t1的无参数方法

java 复制代码
execution(public String com.example.demo.controller.TestController.t1())

2、省略访问修饰符(private不生效)

java 复制代码
execution(public String com.example.demo.controller.TestController.t1())

3、匹配所有的返回类型

java 复制代码
execution(* com.example.demo.controller.TestController.t1())

4、匹配TestController下的所有的无参方法

java 复制代码
execution(* com.example.demo.controller.TestController.*())

5、匹配TestController下的所有方法

java 复制代码
execution(* com.example.demo.controller.TestController.*(..))

6、匹配controller下的所有的类的所有方法

java 复制代码
execution(* String com.example.demo.controller.*.*(..))

7、匹配所有包下的TestController

java 复制代码
execution(* com..TestController.*(..))

8、匹配com.example.demo包下,所有类的所有方法

java 复制代码
execution(* com.example.demo..*(..))

@annotation

实现:自定义一个注解,只要加了这个注解,就能统计方法执行的时间

1、定义一个注解

2、自定义注解

java 复制代码
@Retention(RetentionPolicy.RUNTIME)//表示注解的生命周期
@Target({ElementType.METHOD})//TYPE表示类注解,能加在类上,也能加在方法上;METHOD表示方法注解,只能加在方法上
public @interface TimeRecord {

}

TimeRecordAspect:统计方法执行的时间

java 复制代码
@Slf4j
@Aspect
@Component
//统计方法执行的时间
public class TimeRecordAspect {
    @Around("@annotation(com.wpre.springaop.aspect.TimeRecord)")
    public Object timeRecord(ProceedingJoinPoint pjt) throws Throwable {
        //记录开始时间
        long start = System.currentTimeMillis();

        //执行目标方法
        Object ret = pjt.proceed();

        //记录结束时间
        long end = System.currentTimeMillis();

        log.info("{}耗时:{}ms", pjt.getSignature(), end - start);

        return ret;
    }
}

如果想让所有加了RequestMapping的都实现统计时间的功能,只需要把@Around注解改成如下的内容:

java 复制代码
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

PointCut的使用

定义:

java 复制代码
@Pointcut("execution(* com.wpre.springaop.controller.*.*(..))")
public void pt() {
}

使用:

java 复制代码
@Before("pt()")
public void doBefore() {
    log.info("AspectDemo 执行 doBefore");
}

在其他类中使用,需要加上路径,如果切点是private则其他类无法使用

java 复制代码
@Before("com.wpre.springaop.aspect.AspectDemo.pt()")
public void doBefore() {
    log.info("AspectDemo 执行 doBefore");
}

切点类优先级:按照类名的首字母顺序

通过@Order注解可以修改优先级,数字越大优先级越低

java 复制代码
@Order(1)
public class AspectDemo {
	//.................
}

面试题

1、什么是Spring AOP

2、AOP实现方式

基于代理实现、基于注解实现(自定义注解)、基于配置(< aop:config >)

3、AOP原理

JDK动态代理:Spring AOP可以使用JDK动态代理来创建代理对象,这要求目标对象实现一个接口。

CGLIB代理:当目标对象没有实现接口时,Spring AOP可以使用CGLIB库来创建代理对象。

4、Spring用的哪种代理方式?

Spring的代理方式:

ProxyTargetClass 目标对象 代理方式
false 实现了接口 jdk代理
false 未实现接口 cglib代理
true 实现了接口 cglib代理
true 未实现接口 cglib代理

SpringBoot的代理方式:

从2.x版本开始,默认使用cglib

相关推荐
龙谷情Sinoam1 分钟前
扩展若依@Excel注解,使其对字段的控制是否导出更加便捷
java
二十雨辰13 分钟前
[尚庭公寓]07-Knife快速入门
java·开发语言·spring
掉鱼的猫24 分钟前
Java MCP 实战:构建跨进程与远程的工具服务
java·openai·mcp
我爱Jack1 小时前
时间与空间复杂度详解:算法效率的度量衡
java·开发语言·算法
米饭「」1 小时前
C++AVL树
java·开发语言·c++
Zonda要好好学习1 小时前
Python入门Day4
java·网络·python
SimonKing1 小时前
告别传统读写!RandomAccessFile让你的Java程序快人一步
java·后端·程序员
Little-Hu1 小时前
QML TextEdit组件
java·服务器·数据库
Edingbrugh.南空2 小时前
Flink ClickHouse 连接器数据读取源码深度解析
java·clickhouse·flink
NE_STOP2 小时前
SpringBoot--简单入门
java·spring