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

相关推荐
间彧41 分钟前
SimpleDateFormat既然不推荐使用,为什么java 8+中不删除此类
java
间彧1 小时前
DateTimeFormatter相比SimpleDateFormat在性能上有何差异?
java
间彧1 小时前
为什么说SimpleDateFormat是经典的线程不安全类
java
MacroZheng1 小时前
横空出世!MyBatis-Plus 同款 ES ORM 框架,用起来够优雅!
java·后端·elasticsearch
用户0332126663672 小时前
Java 查找并替换 Excel 中的数据:详细教程
java
间彧2 小时前
ThreadLocal实现原理与应用实践
java
若水不如远方2 小时前
Netty的四种零拷贝机制:深入原理与实战指南
java·netty
用户7493636848432 小时前
【开箱即用】一分钟使用java对接海外大模型gpt等对话模型,实现打字机效果
java
SimonKing2 小时前
一键开启!Spring Boot 的这些「魔法开关」@Enable*,你用对了吗?
java·后端·程序员