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

相关推荐
程序员阿健11 分钟前
风水算命系统架构与功能分析
java·系统架构
计算机-秋大田21 分钟前
基于Spring Boot的城市垃圾分类管理系统设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·spring·课程设计
lmxnsI24 分钟前
java导出pdf文件
java·开发语言·pdf
大飞哥~BigFei38 分钟前
Docker Desktop 构建java8基础镜像jdk安装配置失效解决
java·docker·容器
Waitfor_Me42 分钟前
RabbitMQ介绍与使用
java·rabbitmq
四月天行健1 小时前
【JavaEE】—— SpringBoot项目集成百度千帆AI大模型(对话Chat V2)
java·java-ee·百度千帆大模型·ai大模型
NULL->NEXT1 小时前
Java(day7)
java·开发语言
李老头探索2 小时前
Java 异常机制详解:类型、原理、关键字与最佳实践
java·开发语言
鬼鬼骑士2 小时前
Java实用办公小程序
java·开发语言·小程序
zhangjunli2 小时前
java小写数字转大写
java·服务器